diff options
74 files changed, 2701 insertions, 1447 deletions
diff --git a/Android.mk b/Android.mk index 5c4c2376f80e..73c0233be076 100644 --- a/Android.mk +++ b/Android.mk @@ -312,6 +312,9 @@ $(out_zip): $(full_target) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE)) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE)) +$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt) +$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt) +$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt) # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip. # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to diff --git a/api/current.txt b/api/current.txt index c1a1c52bbbbd..816148a4d15d 100755 --- a/api/current.txt +++ b/api/current.txt @@ -14015,6 +14015,7 @@ package android.graphics { method public android.os.LocaleList getTextLocales(); method public void getTextPath(char[], int, int, float, float, android.graphics.Path); method public void getTextPath(java.lang.String, int, int, float, float, android.graphics.Path); + method public float getTextRunAdvances(char[], int, int, int, int, boolean, float[], int); method public float getTextScaleX(); method public float getTextSize(); method public float getTextSkewX(); @@ -18676,6 +18677,37 @@ package android.icu.text { field public static final int WORD_NUMBER_LIMIT = 200; // 0xc8 } + public abstract class CaseMap { + method public static android.icu.text.CaseMap.Fold fold(); + method public abstract android.icu.text.CaseMap omitUnchangedText(); + method public static android.icu.text.CaseMap.Lower toLower(); + method public static android.icu.text.CaseMap.Title toTitle(); + method public static android.icu.text.CaseMap.Upper toUpper(); + } + + public static final class CaseMap.Fold extends android.icu.text.CaseMap { + method public <A extends java.lang.Appendable> A apply(java.lang.CharSequence, A, android.icu.text.Edits); + method public android.icu.text.CaseMap.Fold omitUnchangedText(); + method public android.icu.text.CaseMap.Fold turkic(); + } + + public static final class CaseMap.Lower extends android.icu.text.CaseMap { + method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits); + method public android.icu.text.CaseMap.Lower omitUnchangedText(); + } + + public static final class CaseMap.Title extends android.icu.text.CaseMap { + method public <A extends java.lang.Appendable> A apply(java.util.Locale, android.icu.text.BreakIterator, java.lang.CharSequence, A, android.icu.text.Edits); + method public android.icu.text.CaseMap.Title noBreakAdjustment(); + method public android.icu.text.CaseMap.Title noLowercase(); + method public android.icu.text.CaseMap.Title omitUnchangedText(); + } + + public static final class CaseMap.Upper extends android.icu.text.CaseMap { + method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits); + method public android.icu.text.CaseMap.Upper omitUnchangedText(); + } + public final class CollationElementIterator { method public int getMaxExpansion(int); method public int getOffset(); @@ -19341,6 +19373,30 @@ package android.icu.text { enum_constant public static final android.icu.text.DisplayContext.Type SUBSTITUTE_HANDLING; } + public final class Edits { + ctor public Edits(); + method public void addReplace(int, int); + method public void addUnchanged(int); + method public android.icu.text.Edits.Iterator getCoarseChangesIterator(); + method public android.icu.text.Edits.Iterator getCoarseIterator(); + method public android.icu.text.Edits.Iterator getFineChangesIterator(); + method public android.icu.text.Edits.Iterator getFineIterator(); + method public boolean hasChanges(); + method public int lengthDelta(); + method public void reset(); + } + + public static final class Edits.Iterator { + method public int destinationIndex(); + method public boolean findSourceIndex(int); + method public boolean hasChange(); + method public int newLength(); + method public boolean next(); + method public int oldLength(); + method public int replacementIndex(); + method public int sourceIndex(); + } + public abstract class IDNA { method public static android.icu.text.IDNA getUTS46Instance(int); method public abstract java.lang.StringBuilder labelToASCII(java.lang.CharSequence, java.lang.StringBuilder, android.icu.text.IDNA.Info); @@ -49697,6 +49753,7 @@ package android.view.accessibility { method public boolean isScrollable(); method public boolean isSelected(); method public boolean isShowingHintText(); + method public boolean isTextEntryKey(); method public boolean isVisibleToUser(); method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View); method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int); @@ -49758,6 +49815,7 @@ package android.view.accessibility { method public void setSource(android.view.View); method public void setSource(android.view.View, int); method public void setText(java.lang.CharSequence); + method public void setTextEntryKey(boolean); method public void setTextSelection(int, int); method public void setTooltipText(java.lang.CharSequence); method public void setTraversalAfter(android.view.View); diff --git a/api/system-current.txt b/api/system-current.txt index d29b2167092f..79bbace4734f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -91,6 +91,7 @@ package android { field public static final java.lang.String KILL_UID = "android.permission.KILL_UID"; field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS"; field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; + field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE"; field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO"; field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS"; @@ -155,6 +156,7 @@ package android { field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER"; field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION"; field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES"; + field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD"; field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS"; field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT"; field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS"; diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index b11e84322dde..e14f2eb41816 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -207,7 +207,14 @@ int main(int argc, char** argv) result = outBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &base); - if (base == NULL) { + if (base == nullptr || result != NO_ERROR) { + String8 reason; + if (base == nullptr) { + reason = "Failed to write to buffer"; + } else { + reason.appendFormat("Error Code: %d", result); + } + fprintf(stderr, "Failed to take screenshot (%s)\n", reason.c_str()); close(fd); return 1; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 6ab4dd915178..c8e6b46d6cff 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -168,6 +168,7 @@ message Atom { AppSize app_size = 10027; CategorySize category_size = 10028; android.service.procstats.ProcessStatsSectionProto proc_stats = 10029; + BatteryVoltage battery_voltage = 10030; } // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above @@ -431,9 +432,9 @@ message BleScanStateChanged { * Logs reporting of a ble scan finding results. * * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + * packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java */ -// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats). +// TODO: Consider also tracking per-scanner-id. message BleScanResultReceived { repeated AttributionNode attribution_node = 1; @@ -2176,6 +2177,16 @@ message FullBatteryCapacity { } /** + * Pulls battery voltage. + * Pulled from: + * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp + */ +message BatteryVoltage { + // The voltage of the battery, in millivolts. + optional int32 voltage_mV = 1; +} + +/** * Pulls the temperature of various parts of the device. * The units are tenths of a degree Celsius. Eg: 30.3C is reported as 303. * @@ -2261,6 +2272,14 @@ message BinderCallsExceptions { optional int64 exception_count = 2; } +/** + * Pulls the statistics of message dispatching on HandlerThreads. + * + * Looper stats will be reset every time the data is pulled. It means it can only be pulled by one + * config on the device. + * + * Next tag: 11 + */ message LooperStats { // Currently not collected and always set to 0. optional int32 uid = 1 [(is_uid) = true]; @@ -2304,8 +2323,11 @@ message LooperStats { // Total CPU usage of all processed message. // Average can be computed using recorded_total_cpu_micros / // recorded_message_count. Total can be computed using - // recorded_total_cpu_micros / recorded_message_count * call_count. + // recorded_total_cpu_micros / recorded_message_count * message_count. optional int64 recorded_total_cpu_micros = 9; + + // True if the screen was interactive PowerManager#isInteractive at the end of the call. + optional bool screen_interactive = 10; } /** diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp index ae97d7a2fc15..ae2cf74962c3 100644 --- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp +++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp @@ -67,6 +67,7 @@ bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* dat data->clear(); bool result_success = true; + // Get the data from the Health HAL (hardware/interfaces/health/1.0/types.hal). Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) { if (r != Result::SUCCESS) { result_success = false; @@ -84,6 +85,12 @@ bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* dat ptr->write(v.legacy.batteryFullCharge); ptr->init(); data->push_back(ptr); + } else if (mTagId == android::util::BATTERY_VOLTAGE) { + auto ptr = make_shared<LogEvent>(android::util::BATTERY_VOLTAGE, + wallClockTimestampNs, elapsedTimestampNs); + ptr->write(v.legacy.batteryVoltage); + ptr->init(); + data->push_back(ptr); } else { ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId); } diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index f6ba0b6017be..745ff74c2623 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -161,6 +161,12 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}, + // battery_voltage + {android::util::BATTERY_VOLTAGE, + {{}, + {}, + 1 * NS_PER_SEC, + new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}}, // process_memory_state {android::util::PROCESS_MEMORY_STATE, {{4, 5, 6, 7, 8}, @@ -184,7 +190,7 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // looper_stats {android::util::LOOPER_STATS, {{5, 6, 7, 8, 9}, - {2, 3, 4}, + {2, 3, 4, 10}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::LOOPER_STATS)}}, // Disk Stats diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 5d37c5fdbaff..0367d9b7dfa1 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -2134,6 +2134,7 @@ Lcom/android/internal/telephony/IWapPushManager;->updatePackage(Ljava/lang/Strin Lcom/android/internal/telephony/SmsHeader$ConcatRef;-><init>()V Lcom/android/internal/telephony/SmsHeader$PortAddrs;-><init>()V Lcom/android/internal/telephony/SmsMessageBase;-><init>()V +Lcom/android/internal/telephony/uicc/IccUtils;->bytesToHexString([B)Ljava/lang/String; Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String; Lcom/android/internal/view/BaseIWindow;-><init>()V @@ -2222,13 +2223,6 @@ Lcom/android/org/conscrypt/AbstractConscryptSocket;->setSoWriteTimeout(I)V Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setHostname(Ljava/lang/String;)V Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setUseSessionTickets(Z)V -Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostname()Ljava/lang/String; -Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostnameOrIP()Ljava/lang/String; -Lcom/android/org/conscrypt/ConscryptSocketBase;->getSoWriteTimeout()I -Lcom/android/org/conscrypt/ConscryptSocketBase;->setHandshakeTimeout(I)V -Lcom/android/org/conscrypt/ConscryptSocketBase;->setHostname(Ljava/lang/String;)V -Lcom/android/org/conscrypt/ConscryptSocketBase;->setSoWriteTimeout(I)V -Lcom/android/org/conscrypt/ConscryptSocketBase;->socket:Ljava/net/Socket; Lcom/android/org/conscrypt/OpenSSLKey;-><init>(J)V Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey; Lcom/android/org/conscrypt/OpenSSLKey;->getNativeRef()Lcom/android/org/conscrypt/NativeRef$EVP_PKEY; @@ -2254,6 +2248,108 @@ Lcom/android/org/conscrypt/TrustedCertificateStore;-><init>()V Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateChain(Ljava/security/cert/X509Certificate;)Ljava/util/List; Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; +Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList; +Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList; +Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList; +Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z +Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z +Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z +Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z +Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z +Lcom/google/android/mms/MmsException;-><init>()V +Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V +Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V +Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V +Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String; +Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B +Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V +Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V +Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V +Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String; +Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue; +Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String; +Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I +Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody; +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue; +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue; +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V +Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V +Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B +Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J +Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue; +Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B +Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J +Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue; +Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B +Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V +Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V +Lcom/google/android/mms/pdu/PduBody;-><init>()V +Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V +Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z +Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart; +Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart; +Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart; +Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart; +Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart; +Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I +Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V +Lcom/google/android/mms/pdu/PduComposer;->make()[B +Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu; +Lcom/google/android/mms/pdu/PduPart;-><init>()V +Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String; +Lcom/google/android/mms/pdu/PduPart;->getCharset()I +Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B +Lcom/google/android/mms/pdu/PduPart;->getContentType()[B +Lcom/google/android/mms/pdu/PduPart;->getData()[B +Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri; +Lcom/google/android/mms/pdu/PduPart;->getFilename()[B +Lcom/google/android/mms/pdu/PduPart;->getName()[B +Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V +Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V +Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V +Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V +Lcom/google/android/mms/pdu/PduPart;->setData([B)V +Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V +Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B +Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister; +Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor; +Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu; +Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri; +Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri; +Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri; +Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String; +Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V +Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V +Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B +Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V +Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V +Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue; +Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B +Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B +Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B +Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I +Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B +Lcom/google/android/mms/pdu/SendReq;-><init>()V +Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue; +Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B +Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V +Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V +Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V +Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V +Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V +Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V +Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object; +Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache; +Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z +Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry; +Lcom/google/android/mms/util/PduCache;->purgeAll()V +Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu; +Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri; Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V @@ -2560,6 +2656,7 @@ Ljava/net/Socket;->impl:Ljava/net/SocketImpl; Ljava/net/SocketException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket; Ljava/net/SocketImpl;->socket:Ljava/net/Socket; +Ljava/net/SocksSocketImpl;-><init>()V Ljava/net/URI;->fragment:Ljava/lang/String; Ljava/net/URI;->host:Ljava/lang/String; Ljava/net/URI;->port:I diff --git a/config/preloaded-classes b/config/preloaded-classes index 50e97c53fe83..63c583f9264c 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -1436,6 +1436,12 @@ android.hardware.usb.UsbDevice$1 android.hardware.usb.UsbDeviceConnection android.hardware.usb.UsbManager android.hardware.usb.UsbRequest +android.hidl.base.V1_0.DebugInfo +android.hidl.base.V1_0.IBase +android.hidl.manager.V1_0.IServiceManager +android.hidl.manager.V1_0.IServiceManager$Proxy +android.hidl.manager.V1_0.IServiceNotification +android.hidl.manager.V1_0.IServiceNotification$Stub android.icu.impl.BMPSet android.icu.impl.CacheBase android.icu.impl.CacheValue diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 5499d59fe0b3..2ee266de4c77 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -545,12 +545,12 @@ import java.util.List; * <a name="SavingPersistentState"></a> * <h3>Saving Persistent State</h3> * - * <p>There are generally two kinds of persistent state than an activity + * <p>There are generally two kinds of persistent state that an activity * will deal with: shared document-like data (typically stored in a SQLite * database using a {@linkplain android.content.ContentProvider content provider}) * and internal state such as user preferences.</p> * - * <p>For content provider data, we suggest that activities use a + * <p>For content provider data, we suggest that activities use an * "edit in place" user model. That is, any edits a user makes are effectively * made immediately without requiring an additional confirmation step. * Supporting this model is generally a simple matter of following two rules:</p> @@ -1383,6 +1383,7 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this); getApplication().dispatchActivityResumed(this); mActivityTransitionState.onResume(this, isTopOfTask()); + enableAutofillCompatibilityIfNeeded(); if (mAutoFillResetNeeded) { if (!mAutoFillIgnoreFirstResumePause) { View focus = getCurrentFocus(); @@ -7165,7 +7166,6 @@ public class Activity extends ContextThemeWrapper mWindow.setColorMode(info.colorMode); setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled()); - enableAutofillCompatibilityIfNeeded(); } private void enableAutofillCompatibilityIfNeeded() { diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 0dd618657004..0469a9006b25 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -125,7 +125,7 @@ import java.util.Set; * * <p><strong>Data Authority</strong> matches if any of the given values match * the Intent's data authority <em>and</em> one of the data schemes in the filter - * has matched the Intent, <em>or</em> no authories were supplied in the filter. + * has matched the Intent, <em>or</em> no authorities were supplied in the filter. * The Intent authority is determined by calling * {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI. * <em>Note that authority matching here is <b>case sensitive</b>, unlike diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 1b4878c5e89f..a15711f5da50 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1979,6 +1979,8 @@ public abstract class PackageManager { * </ul> * A version of 1.1.0 or higher also indicates: * <ul> + * <li>The {@code VK_ANDROID_external_memory_android_hardware_buffer} extension is + * supported.</li> * <li>{@code SYNC_FD} external semaphore and fence handles are supported.</li> * <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is * supported.</li> diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4c7f0f3d823d..aa178fb9ff36 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11537,14 +11537,6 @@ public final class Settings { public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed"; /** - * Enable faster emergency phone call feature. - * The value is a boolean (1 or 0). - * @hide - */ - public static final String FASTER_EMERGENCY_PHONE_CALL_ENABLED = - "faster_emergency_phone_call_enabled"; - - /** * See RIL_PreferredNetworkType in ril.h * @hide */ diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index ff6e86ebff61..183e83304925 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -35,6 +35,7 @@ public class FeatureFlagUtils { public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override."; public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX; public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; + public static final String EMERGENCY_DIAL_SHORTCUTS = "settings_emergency_dial_shortcuts"; private static final Map<String, String> DEFAULT_FLAGS; static { @@ -47,6 +48,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_data_usage_v2", "false"); DEFAULT_FLAGS.put("settings_seamless_transfer", "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true"); + DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "false"); } /** diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index a3fa2ceba4d3..4d3f0fc20ecd 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -632,6 +632,8 @@ public class AccessibilityNodeInfo implements Parcelable { private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x0200000; + private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000; + /** * Bits that provide the id of a virtual descendant of a view. */ @@ -2461,6 +2463,30 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Returns whether node represents a text entry key that is part of a keyboard or keypad. + * + * @return {@code true} if the node is a text entry key., {@code false} otherwise. + */ + public boolean isTextEntryKey() { + return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY); + } + + /** + * Sets whether the node represents a text entry key that is part of a keyboard or keypad. + * + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param isTextEntryKey {@code true} if the node is a text entry key, {@code false} otherwise. + */ + public void setTextEntryKey(boolean isTextEntryKey) { + setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY, isTextEntryKey); + } + + /** * Gets the package this node comes from. * * @return The package name. diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 9419e93d0066..e87048e1a5cb 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -45,6 +45,7 @@ import android.service.autofill.UserData; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Slog; import android.util.SparseArray; import android.view.Choreographer; import android.view.KeyEvent; @@ -559,6 +560,9 @@ public final class AutofillManager { // different bridge based on which activity is currently focused // in the current process. Since compat would be rarely used, just // create and register a new instance every time. + if (sDebug) { + Slog.d(TAG, "creating CompatibilityBridge for " + mContext); + } mCompatibilityBridge = new CompatibilityBridge(); } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index d0795c95a8ad..31f13c33c167 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -5758,8 +5758,6 @@ public class BatteryStatsImpl extends BatteryStats { for (int i = 0; i < N; i++) { int uid = mapUid(ws.get(i)); getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults); - StatsLog.write_non_chained(StatsLog.BLE_SCAN_RESULT_RECEIVED, ws.get(i), ws.getName(i), - numNewResults); } final List<WorkChain> workChains = ws.getWorkChains(); @@ -5768,8 +5766,6 @@ public class BatteryStatsImpl extends BatteryStats { final WorkChain wc = workChains.get(i); int uid = mapUid(wc.getAttributionUid()); getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults); - StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, - wc.getUids(), wc.getTags(), numNewResults); } } } diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java index 02a8b224e3c2..0650d0af7caf 100644 --- a/core/java/com/android/internal/os/LooperStats.java +++ b/core/java/com/android/internal/os/LooperStats.java @@ -106,6 +106,7 @@ public class LooperStats implements Looper.Observer { synchronized (entry) { entry.exceptionCount++; } + recycleSession(session); } @@ -116,29 +117,29 @@ public class LooperStats implements Looper.Observer { /** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */ public List<ExportedEntry> getEntries() { - final ArrayList<ExportedEntry> entries; + final ArrayList<ExportedEntry> exportedEntries; synchronized (mLock) { final int size = mEntries.size(); - entries = new ArrayList<>(size); + exportedEntries = new ArrayList<>(size); for (int i = 0; i < size; i++) { Entry entry = mEntries.valueAt(i); synchronized (entry) { - entries.add(new ExportedEntry(entry)); + exportedEntries.add(new ExportedEntry(entry)); } } } // Add the overflow and collision entries only if they have any data. - if (mOverflowEntry.messageCount > 0 || mOverflowEntry.exceptionCount > 0) { - synchronized (mOverflowEntry) { - entries.add(new ExportedEntry(mOverflowEntry)); - } - } - if (mHashCollisionEntry.messageCount > 0 || mHashCollisionEntry.exceptionCount > 0) { - synchronized (mHashCollisionEntry) { - entries.add(new ExportedEntry(mHashCollisionEntry)); + maybeAddSpecialEntry(exportedEntries, mOverflowEntry); + maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry); + return exportedEntries; + } + + private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) { + synchronized (specialEntry) { + if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) { + exportedEntries.add(new ExportedEntry(specialEntry)); } } - return entries; } /** Removes all collected data. */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 814973d783c2..1df3f7fcd6ca 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3054,6 +3054,18 @@ <permission android:name="android.permission.MANAGE_DEVICE_ADMINS" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an app to reset the device password. + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.RESET_PASSWORD" + android:protectionLevel="signature|privileged" /> + + <!-- @SystemApi Allows an app to lock the device. + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.LOCK_DEVICE" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows low-level access to setting the orientation (actually rotation) of the screen. <p>Not for use by third-party applications. diff --git a/core/res/res/values-mcc302-mnc370/strings.xml b/core/res/res/values-mcc302-mnc370/strings.xml deleted file mode 100644 index f5b8496f9a0b..000000000000 --- a/core/res/res/values-mcc302-mnc370/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Template for showing mobile network operator name while WFC is active --> - <string-array name="wfcSpnFormats"> - <item>%s</item> - <item>%s Wi-Fi</item> - </string-array> -</resources> diff --git a/core/res/res/values-mcc302-mnc720/strings.xml b/core/res/res/values-mcc302-mnc720/strings.xml deleted file mode 100644 index f5b8496f9a0b..000000000000 --- a/core/res/res/values-mcc302-mnc720/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Template for showing mobile network operator name while WFC is active --> - <string-array name="wfcSpnFormats"> - <item>%s</item> - <item>%s Wi-Fi</item> - </string-array> -</resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 293d90e276b3..c4f68e3d184e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -913,7 +913,7 @@ <!-- Control whether to lock day/night mode change from normal application. When it is true, day / night mode change is only allowed to apps with MODIFY_DAY_NIGHT_MODE permission. --> - <bool name="config_lockDayNightMode">true</bool> + <bool name="config_lockDayNightMode">false</bool> <!-- Control the default night mode to use when there is no other mode override set. One of the following values (see UiModeManager.java): diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 1a5b7b6cb06a..64620f33b2df 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -240,10 +240,31 @@ <item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item> </string-array> <!-- Template for showing mobile network operator name while WFC is active --> - <string-array name="wfcSpnFormats"> - <item>%s</item> - <item>%s Wi-Fi Calling</item> + <string-array name="wfcSpnFormats" translatable="false"> + <item>@string/wfcSpnFormat_spn</item> + <item>@string/wfcSpnFormat_spn_wifi_calling</item> + <item>@string/wfcSpnFormat_wlan_call</item> + <item>@string/wfcSpnFormat_spn_wlan_call</item> + <item>@string/wfcSpnFormat_spn_wifi</item> + <item>@string/wfcSpnFormat_wifi_calling_bar_spn</item> + <item>@string/wfcSpnFormat_spn_vowifi</item> </string-array> + + <!-- Spn during Wi-Fi Calling: "<operator>" --> + <string name="wfcSpnFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string> + <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi Calling" --> + <string name="wfcSpnFormat_spn_wifi_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi Calling</string> + <!-- Spn during Wi-Fi Calling: "WLAN Call" --> + <string name="wfcSpnFormat_wlan_call">WLAN Call</string> + <!-- Spn during Wi-Fi Calling: "<operator> WLAN Call" --> + <string name="wfcSpnFormat_spn_wlan_call"><xliff:g id="spn" example="Operator">%s</xliff:g> WLAN Call</string> + <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi" --> + <string name="wfcSpnFormat_spn_wifi"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi</string> + <!-- Spn during Wi-Fi Calling: "WiFi Calling | <operator>" --> + <string name="wfcSpnFormat_wifi_calling_bar_spn">WiFi Calling | <xliff:g id="spn" example="Operator">%s</xliff:g></string> + <!-- Spn during Wi-Fi Calling: "<operator> VoWifi" --> + <string name="wfcSpnFormat_spn_vowifi"><xliff:g id="spn" example="Operator">%s</xliff:g> VoWifi</string> + <!-- WFC, summary for Disabled --> <string name="wifi_calling_off_summary">Off</string> <!-- WFC, summary for Wi-Fi Preferred --> diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java index 2f28606790cf..b5ed01f708b1 100644 --- a/core/tests/coretests/src/android/graphics/PaintTest.java +++ b/core/tests/coretests/src/android/graphics/PaintTest.java @@ -186,44 +186,28 @@ public class PaintTest extends InstrumentationTestCase { Paint p = new Paint(); final int count = end - start; - final float[][] advanceArrays = new float[4][count]; - - final float advance = p.getTextRunAdvances(str, start, end, contextStart, contextEnd, - isRtl, advanceArrays[0], 0); - + final int contextCount = contextEnd - contextStart; + final float[][] advanceArrays = new float[2][count]; char chars[] = str.toCharArray(); - final float advance_c = p.getTextRunAdvances(chars, start, count, contextStart, - contextEnd - contextStart, isRtl, advanceArrays[1], 0); - assertEquals(advance, advance_c, 1.0f); - + final float advance = p.getTextRunAdvances(chars, start, count, + contextStart, contextCount, isRtl, advanceArrays[0], 0); for (int c = 1; c < count; ++c) { - final float firstPartAdvance = p.getTextRunAdvances(str, start, start + c, - contextStart, contextEnd, isRtl, advanceArrays[2], 0); - final float secondPartAdvance = p.getTextRunAdvances(str, start + c, end, - contextStart, contextEnd, isRtl, advanceArrays[2], c); + final float firstPartAdvance = p.getTextRunAdvances(chars, start, c, + contextStart, contextCount, isRtl, advanceArrays[1], 0); + final float secondPartAdvance = p.getTextRunAdvances(chars, start + c, count - c, + contextStart, contextCount, isRtl, advanceArrays[1], c); assertEquals(advance, firstPartAdvance + secondPartAdvance, 1.0f); - - final float firstPartAdvance_c = p.getTextRunAdvances(chars, start, c, - contextStart, contextEnd - contextStart, isRtl, advanceArrays[3], 0); - final float secondPartAdvance_c = p.getTextRunAdvances(chars, start + c, - count - c, contextStart, contextEnd - contextStart, isRtl, - advanceArrays[3], c); - assertEquals(advance, firstPartAdvance_c + secondPartAdvance_c, 1.0f); - assertEquals(firstPartAdvance, firstPartAdvance_c, 1.0f); - assertEquals(secondPartAdvance, secondPartAdvance_c, 1.0f); - - for (int i = 1; i < advanceArrays.length; i++) { - for (int j = 0; j < count; j++) { - assertEquals(advanceArrays[0][j], advanceArrays[i][j], 1.0f); - } + for (int j = 0; j < count; j++) { + assertEquals(advanceArrays[0][j], advanceArrays[1][j], 1.0f); } + // Compare results with measureText, getRunAdvance, and getTextWidths. if (compareWithOtherMethods && start == contextStart && end == contextEnd) { assertEquals(advance, p.measureText(str, start, end), 1.0f); assertEquals(advance, p.getRunAdvance( - str, start, end, contextStart, contextEnd, isRtl, end), 1.0f); + chars, start, count, contextStart, contextCount, isRtl, end), 1.0f); final float[] widths = new float[count]; p.getTextWidths(str, start, end, widths); @@ -236,19 +220,7 @@ public class PaintTest extends InstrumentationTestCase { public void testGetTextRunAdvances_invalid() { Paint p = new Paint(); - String text = "test"; - - try { - p.getTextRunAdvances((String)null, 0, 0, 0, 0, false, null, 0); - fail("Should throw an IllegalArgumentException."); - } catch (IllegalArgumentException e) { - } - - try { - p.getTextRunAdvances((CharSequence)null, 0, 0, 0, 0, false, null, 0); - fail("Should throw an IllegalArgumentException."); - } catch (IllegalArgumentException e) { - } + char[] text = "test".toCharArray(); try { p.getTextRunAdvances((char[])null, 0, 0, 0, 0, false, null, 0); @@ -257,50 +229,43 @@ public class PaintTest extends InstrumentationTestCase { } try { - p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false, - new float[text.length() - 1], 0); + p.getTextRunAdvances(text, 0, text.length, 0, text.length, false, + new float[text.length - 1], 0); fail("Should throw an IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { } try { - p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false, - new float[text.length()], 1); + p.getTextRunAdvances(text, 0, text.length, 0, text.length, false, + new float[text.length], 1); fail("Should throw an IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { } // 0 > contextStart try { - p.getTextRunAdvances(text, 0, text.length(), -1, text.length(), false, null, 0); + p.getTextRunAdvances(text, 0, text.length, -1, text.length, false, null, 0); fail("Should throw an IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { } // contextStart > start try { - p.getTextRunAdvances(text, 0, text.length(), 1, text.length(), false, null, 0); - fail("Should throw an IndexOutOfBoundsException."); - } catch (IndexOutOfBoundsException e) { - } - - // start > end - try { - p.getTextRunAdvances(text, 1, 0, 0, text.length(), false, null, 0); + p.getTextRunAdvances(text, 0, text.length, 1, text.length, false, null, 0); fail("Should throw an IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { } // end > contextEnd try { - p.getTextRunAdvances(text, 0, text.length(), 0, text.length() - 1, false, null, 0); + p.getTextRunAdvances(text, 0, text.length, 0, text.length - 1, false, null, 0); fail("Should throw an IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { } // contextEnd > text.length try { - p.getTextRunAdvances(text, 0, text.length(), 0, text.length() + 1, false, null, 0); + p.getTextRunAdvances(text, 0, text.length, 0, text.length + 1, false, null, 0); fail("Should throw an IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index e84aed1b2e38..fee470dce24d 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -241,7 +241,6 @@ public class SettingsBackupTest { Settings.Global.EUICC_SUPPORTED_COUNTRIES, Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS, Settings.Global.FANCY_IME_ANIMATIONS, - Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, Settings.Global.FORCE_ALLOW_ON_EXTERNAL, Settings.Global.FORCED_APP_STANDBY_ENABLED, Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java index 922b79aa008d..69d2828f20bc 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java @@ -57,7 +57,7 @@ public class AccessibilityNodeInfoTest { // The number of flags held in boolean properties. Their values should also be double-checked // in the methods above. - private static final int NUM_BOOLEAN_PROPERTIES = 17; + private static final int NUM_BOOLEAN_PROPERTIES = 18; @Test public void testStandardActions_serializationFlagIsValid() { diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index 0eb3d06e79de..565a3ecd0411 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -342,6 +342,20 @@ public final class LooperStatsTest { assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(2); } + @Test + public void testReset() { + TestableLooperStats looperStats = new TestableLooperStats(1, 1); + + Object token1 = looperStats.messageDispatchStarting(); + looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000)); + Object token2 = looperStats.messageDispatchStarting(); + looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(2000)); + looperStats.reset(); + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + assertThat(entries).hasSize(0); + } + private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { try { r.run(); diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 6f306530868d..85b39fea8427 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -17,7 +17,9 @@ package android.graphics; import android.annotation.ColorInt; +import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.Px; import android.annotation.Size; import android.annotation.UnsupportedAppUsage; @@ -2325,17 +2327,53 @@ public class Paint { } /** - * Convenience overload that takes a char array instead of a - * String. + * Retrieve the character advances of the text. * - * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int) - * @hide - */ - @UnsupportedAppUsage - public float getTextRunAdvances(char[] chars, int index, int count, - int contextIndex, int contextCount, boolean isRtl, float[] advances, - int advancesIndex) { - + * Returns the total advance width for the characters in the run from {@code index} for + * {@code count} of chars, and if {@code advances} is not null, the advance assigned to each of + * these characters (java chars). + * + * <p> + * The trailing surrogate in a valid surrogate pair is assigned an advance of 0. Thus the + * number of returned advances is always equal to count, not to the number of unicode codepoints + * represented by the run. + * </p> + * + * <p> + * In the case of conjuncts or combining marks, the total advance is assigned to the first + * logical character, and the following characters are assigned an advance of 0. + * </p> + * + * <p> + * This generates the sum of the advances of glyphs for characters in a reordered cluster as the + * width of the first logical character in the cluster, and 0 for the widths of all other + * characters in the cluster. In effect, such clusters are treated like conjuncts. + * </p> + * + * <p> + * The shaping bounds limit the amount of context available outside start and end that can be + * used for shaping analysis. These bounds typically reflect changes in bidi level or font + * metrics across which shaping does not occur. + * </p> + * + * @param chars the text to measure. + * @param index the index of the first character to measure + * @param count the number of characters to measure + * @param contextIndex the index of the first character to use for shaping context. + * Context must cover the measureing target. + * @param contextCount the number of character to use for shaping context. + * Context must cover the measureing target. + * @param isRtl whether the run is in RTL direction + * @param advances array to receive the advances, must have room for all advances. + * This can be null if only total advance is needed + * @param advancesIndex the position in advances at which to put the advance corresponding to + * the character at start + * @return the total advance in pixels + */ + public float getTextRunAdvances(@NonNull char[] chars, @IntRange(from = 0) int index, + @IntRange(from = 0) int count, @IntRange(from = 0) int contextIndex, + @IntRange(from = 0) int contextCount, boolean isRtl, @Nullable float[] advances, + @IntRange(from = 0) int advancesIndex) { if (chars == null) { throw new IllegalArgumentException("text cannot be null"); } @@ -2372,131 +2410,6 @@ public class Paint { } /** - * Convenience overload that takes a CharSequence instead of a - * String. - * - * @see #getTextRunAdvances(String, int, int, int, int, boolean, float[], int) - * @hide - */ - public float getTextRunAdvances(CharSequence text, int start, int end, - int contextStart, int contextEnd, boolean isRtl, float[] advances, - int advancesIndex) { - if (text == null) { - throw new IllegalArgumentException("text cannot be null"); - } - if ((start | end | contextStart | contextEnd | advancesIndex | (end - start) - | (start - contextStart) | (contextEnd - end) - | (text.length() - contextEnd) - | (advances == null ? 0 : - (advances.length - advancesIndex - (end - start)))) < 0) { - throw new IndexOutOfBoundsException(); - } - - if (text instanceof String) { - return getTextRunAdvances((String) text, start, end, - contextStart, contextEnd, isRtl, advances, advancesIndex); - } - if (text instanceof SpannedString || - text instanceof SpannableString) { - return getTextRunAdvances(text.toString(), start, end, - contextStart, contextEnd, isRtl, advances, advancesIndex); - } - if (text instanceof GraphicsOperations) { - return ((GraphicsOperations) text).getTextRunAdvances(start, end, - contextStart, contextEnd, isRtl, advances, advancesIndex, this); - } - if (text.length() == 0 || end == start) { - return 0f; - } - - int contextLen = contextEnd - contextStart; - int len = end - start; - char[] buf = TemporaryBuffer.obtain(contextLen); - TextUtils.getChars(text, contextStart, contextEnd, buf, 0); - float result = getTextRunAdvances(buf, start - contextStart, len, - 0, contextLen, isRtl, advances, advancesIndex); - TemporaryBuffer.recycle(buf); - return result; - } - - /** - * Returns the total advance width for the characters in the run - * between start and end, and if advances is not null, the advance - * assigned to each of these characters (java chars). - * - * <p>The trailing surrogate in a valid surrogate pair is assigned - * an advance of 0. Thus the number of returned advances is - * always equal to count, not to the number of unicode codepoints - * represented by the run. - * - * <p>In the case of conjuncts or combining marks, the total - * advance is assigned to the first logical character, and the - * following characters are assigned an advance of 0. - * - * <p>This generates the sum of the advances of glyphs for - * characters in a reordered cluster as the width of the first - * logical character in the cluster, and 0 for the widths of all - * other characters in the cluster. In effect, such clusters are - * treated like conjuncts. - * - * <p>The shaping bounds limit the amount of context available - * outside start and end that can be used for shaping analysis. - * These bounds typically reflect changes in bidi level or font - * metrics across which shaping does not occur. - * - * @param text the text to measure. Cannot be null. - * @param start the index of the first character to measure - * @param end the index past the last character to measure - * @param contextStart the index of the first character to use for shaping context, - * must be <= start - * @param contextEnd the index past the last character to use for shaping context, - * must be >= end - * @param isRtl whether the run is in RTL direction - * @param advances array to receive the advances, must have room for all advances, - * can be null if only total advance is needed - * @param advancesIndex the position in advances at which to put the - * advance corresponding to the character at start - * @return the total advance - * - * @hide - */ - public float getTextRunAdvances(String text, int start, int end, int contextStart, - int contextEnd, boolean isRtl, float[] advances, int advancesIndex) { - if (text == null) { - throw new IllegalArgumentException("text cannot be null"); - } - if ((start | end | contextStart | contextEnd | advancesIndex | (end - start) - | (start - contextStart) | (contextEnd - end) - | (text.length() - contextEnd) - | (advances == null ? 0 : - (advances.length - advancesIndex - (end - start)))) < 0) { - throw new IndexOutOfBoundsException(); - } - - if (text.length() == 0 || start == end) { - return 0f; - } - - if (!mHasCompatScaling) { - return nGetTextAdvances(mNativePaint, text, start, end, contextStart, contextEnd, - isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex); - } - - final float oldSize = getTextSize(); - setTextSize(oldSize * mCompatScaling); - final float totalAdvance = nGetTextAdvances(mNativePaint, text, start, end, contextStart, - contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances, advancesIndex); - setTextSize(oldSize); - - if (advances != null) { - for (int i = advancesIndex, e = i + (end - start); i < e; i++) { - advances[i] *= mInvCompatScaling; - } - } - return totalAdvance * mInvCompatScaling; // assume errors are insignificant - } - - /** * Returns the next cursor position in the run. This avoids placing the * cursor between surrogates, between characters that form conjuncts, * between base characters and combining marks, or within a reordering diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index d1d605fc8399..1d27c03e1dcb 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -43,6 +43,8 @@ import java.nio.ReadOnlyBufferException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** MediaCodec class can be used to access low-level media codecs, i.e. encoder/decoder components. @@ -3582,7 +3584,18 @@ final public class MediaCodec { } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private long mNativeContext; + private long mNativeContext = 0; + private final Lock mNativeContextLock = new ReentrantLock(); + + private final long lockAndGetContext() { + mNativeContextLock.lock(); + return mNativeContext; + } + + private final void setAndUnlockContext(long context) { + mNativeContext = context; + mNativeContextLock.unlock(); + } /** @hide */ public static class MediaImage extends Image { diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index 6ae4d408fca2..61879003b8dc 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -544,29 +544,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { private static final int INVOKE_ID_GET_SELECTED_TRACK = 7; /** - * Create a request parcel which can be routed to the native media - * player using {@link #invoke(Parcel, Parcel)}. The Parcel - * returned has the proper InterfaceToken set. The caller should - * not overwrite that token, i.e it can only append data to the - * Parcel. - * - * @return A parcel suitable to hold a request for the native - * player. - * {@hide} - */ - @Override - public Parcel newRequest() { - Parcel parcel = Parcel.obtain(); - return parcel; - } - - /** * Invoke a generic method on the native player using opaque protocol * buffer message for the request and reply. Both payloads' format is a * convention between the java caller and the native player. * - * @param request PlayerMessage for the extension. The - * caller must use {@link #newRequest()} to get one. + * @param msg PlayerMessage for the extension. * * @return PlayerMessage with the data returned by the * native player. @@ -1536,91 +1518,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } /** - * Gets the media metadata. - * - * @param update_only controls whether the full set of available - * metadata is returned or just the set that changed since the - * last call. See {@see #METADATA_UPDATE_ONLY} and {@see - * #METADATA_ALL}. - * - * @param apply_filter if true only metadata that matches the - * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see - * #BYPASS_METADATA_FILTER}. - * - * @return The metadata, possibly empty. null if an error occured. - // FIXME: unhide. - * {@hide} - */ - @Override - public Metadata getMetadata(final boolean update_only, - final boolean apply_filter) { - Parcel reply = Parcel.obtain(); - Metadata data = new Metadata(); - - if (!native_getMetadata(update_only, apply_filter, reply)) { - reply.recycle(); - return null; - } - - // Metadata takes over the parcel, don't recycle it unless - // there is an error. - if (!data.parse(reply)) { - reply.recycle(); - return null; - } - return data; - } - - /** - * Set a filter for the metadata update notification and update - * retrieval. The caller provides 2 set of metadata keys, allowed - * and blocked. The blocked set always takes precedence over the - * allowed one. - * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as - * shorthands to allow/block all or no metadata. - * - * By default, there is no filter set. - * - * @param allow Is the set of metadata the client is interested - * in receiving new notifications for. - * @param block Is the set of metadata the client is not interested - * in receiving new notifications for. - * @return The call status code. - * - // FIXME: unhide. - * {@hide} - */ - @Override - public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) { - // Do our serialization manually instead of calling - // Parcel.writeArray since the sets are made of the same type - // we avoid paying the price of calling writeValue (used by - // writeArray) which burns an extra int per element to encode - // the type. - Parcel request = newRequest(); - - // The parcel starts already with an interface token. There - // are 2 filters. Each one starts with a 4bytes number to - // store the len followed by a number of int (4 bytes as well) - // representing the metadata type. - int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size()); - - if (request.dataCapacity() < capacity) { - request.setDataCapacity(capacity); - } - - request.writeInt(allow.size()); - for(Integer t: allow) { - request.writeInt(t); - } - request.writeInt(block.size()); - for(Integer t: block) { - request.writeInt(t); - } - return native_setMetadataFilter(request); - } - - /** * Resets the MediaPlayer2 to its uninitialized state. After calling * this method, you will have to initialize it again by setting the * data source and calling prepare(). @@ -1802,32 +1699,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { private native void _setAuxEffectSendLevel(float level); - /* - * @param update_only If true fetch only the set of metadata that have - * changed since the last invocation of getMetadata. - * The set is built using the unfiltered - * notifications the native player sent to the - * MediaPlayer2Manager during that period of - * time. If false, all the metadatas are considered. - * @param apply_filter If true, once the metadata set has been built based on - * the value update_only, the current filter is applied. - * @param reply[out] On return contains the serialized - * metadata. Valid only if the call was successful. - * @return The status code. - */ - private native final boolean native_getMetadata(boolean update_only, - boolean apply_filter, - Parcel reply); - - /* - * @param request Parcel with the 2 serialized lists of allowed - * metadata types followed by the one to be - * dropped. Each list starts with an integer - * indicating the number of metadata type elements. - * @return The status code. - */ - private native final int native_setMetadataFilter(Parcel request); - private static native final void native_init(); private native final void native_setup(Object mediaplayer2_this); private native final void native_finalize(); @@ -1903,25 +1774,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { mFormat = format; } - /** - * Flatten this object in to a Parcel. - * - * @param dest The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. - * May be 0 or {@link android.os.Parcelable#PARCELABLE_WRITE_RETURN_VALUE}. - */ - /* package private */ void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mTrackType); - dest.writeString(getLanguage()); - - if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { - dest.writeString(mFormat.getString(MediaFormat.KEY_MIME)); - dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT)); - dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT)); - dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE)); - } - } - @Override public String toString() { StringBuilder out = new StringBuilder(128); diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 3490ff8fcf43..503720939113 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -108,8 +108,9 @@ static struct { } gCodecInfo; struct fields_t { - jfieldID context; jmethodID postEventFromNativeID; + jmethodID lockAndGetContextID; + jmethodID setAndUnlockContextID; jfieldID cryptoInfoNumSubSamplesID; jfieldID cryptoInfoNumBytesOfClearDataID; jfieldID cryptoInfoNumBytesOfEncryptedDataID; @@ -931,7 +932,7 @@ using namespace android; static sp<JMediaCodec> setMediaCodec( JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) { - sp<JMediaCodec> old = (JMediaCodec *)env->GetLongField(thiz, gFields.context); + sp<JMediaCodec> old = (JMediaCodec *)env->CallLongMethod(thiz, gFields.lockAndGetContextID); if (codec != NULL) { codec->incStrong(thiz); } @@ -944,13 +945,15 @@ static sp<JMediaCodec> setMediaCodec( old->release(); old->decStrong(thiz); } - env->SetLongField(thiz, gFields.context, (jlong)codec.get()); + env->CallVoidMethod(thiz, gFields.setAndUnlockContextID, (jlong)codec.get()); return old; } static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) { - return (JMediaCodec *)env->GetLongField(thiz, gFields.context); + sp<JMediaCodec> codec = (JMediaCodec *)env->CallLongMethod(thiz, gFields.lockAndGetContextID); + env->CallVoidMethod(thiz, gFields.setAndUnlockContextID, (jlong)codec.get()); + return codec; } static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { @@ -1876,15 +1879,21 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { env, env->FindClass("android/media/MediaCodec")); CHECK(clazz.get() != NULL); - gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); - CHECK(gFields.context != NULL); - gFields.postEventFromNativeID = env->GetMethodID( clazz.get(), "postEventFromNative", "(IIILjava/lang/Object;)V"); - CHECK(gFields.postEventFromNativeID != NULL); + gFields.lockAndGetContextID = + env->GetMethodID( + clazz.get(), "lockAndGetContext", "()J"); + CHECK(gFields.lockAndGetContextID != NULL); + + gFields.setAndUnlockContextID = + env->GetMethodID( + clazz.get(), "setAndUnlockContext", "(J)V"); + CHECK(gFields.setAndUnlockContextID != NULL); + jfieldID field; field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_UNENCRYPTED", "I"); CHECK(field != NULL); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index e8f47e18b4e3..5a64e02cece4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -211,6 +211,26 @@ public class CachedBluetoothDeviceManager { } } + /** + * Attempts to get the name of a remote device, otherwise returns the address. + * + * @param device The remote device. + * @return The name, or if unavailable, the address. + */ + public String getName(BluetoothDevice device) { + CachedBluetoothDevice cachedDevice = findDevice(device); + if (cachedDevice != null && cachedDevice.getName() != null) { + return cachedDevice.getName(); + } + + String name = device.getAliasName(); + if (name != null) { + return name; + } + + return device.getAddress(); + } + public synchronized void clearNonBondedDevices() { mCachedDevicesMapForHearingAids.entrySet().removeIf(entries diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java index 8c4bff5bc4ff..c8d4fc84f4b4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java @@ -29,7 +29,7 @@ import com.android.settingslib.R; import java.util.List; /** - * HidProfile handles Bluetooth HID profile. + * HidDeviceProfile handles Bluetooth HID Device role */ public class HidDeviceProfile implements LocalBluetoothProfile { private static final String TAG = "HidDeviceProfile"; @@ -37,7 +37,6 @@ public class HidDeviceProfile implements LocalBluetoothProfile { private static final int ORDINAL = 18; // HID Device Profile is always preferred. private static final int PREFERRED_VALUE = -1; - private static final boolean DEBUG = true; private final CachedBluetoothDeviceManager mDeviceManager; private final LocalBluetoothProfileManager mProfileManager; @@ -59,9 +58,7 @@ public class HidDeviceProfile implements LocalBluetoothProfile { implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (DEBUG) { - Log.d(TAG,"Bluetooth service connected :-)"); - } + Log.d(TAG, "Bluetooth service connected :-), profile:" + profile); mService = (BluetoothHidDevice) proxy; // We just bound to the service, so refresh the UI for any connected HID devices. List<BluetoothDevice> deviceList = mService.getConnectedDevices(); @@ -81,9 +78,7 @@ public class HidDeviceProfile implements LocalBluetoothProfile { } public void onServiceDisconnected(int profile) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service disconnected"); - } + Log.d(TAG, "Bluetooth service disconnected, profile:" + profile); mIsProfileReady = false; } } @@ -110,6 +105,7 @@ public class HidDeviceProfile implements LocalBluetoothProfile { @Override public boolean connect(BluetoothDevice device) { + // Don't invoke method in service because settings is not allowed to connect this profile. return false; } @@ -126,11 +122,7 @@ public class HidDeviceProfile implements LocalBluetoothProfile { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; } - List<BluetoothDevice> deviceList = mService.getConnectedDevices(); - - return !deviceList.isEmpty() && deviceList.contains(device) - ? mService.getConnectionState(device) - : BluetoothProfile.STATE_DISCONNECTED; + return mService.getConnectionState(device); } @Override @@ -185,9 +177,7 @@ public class HidDeviceProfile implements LocalBluetoothProfile { } protected void finalize() { - if (DEBUG) { - Log.d(TAG, "finalize()"); - } + Log.d(TAG, "finalize()"); if (mService != null) { try { BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_DEVICE, diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java index 7d334eb31a19..7ad2e28c84b5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java @@ -32,11 +32,10 @@ import java.util.ArrayList; import java.util.List; /** - * MapClientProfile handles Bluetooth MAP profile. + * MapClientProfile handles the Bluetooth MAP MCE role. */ public final class MapClientProfile implements LocalBluetoothProfile { private static final String TAG = "MapClientProfile"; - private static boolean V = false; private BluetoothMapClient mService; private boolean mIsProfileReady; @@ -60,7 +59,7 @@ public final class MapClientProfile implements LocalBluetoothProfile { implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (V) Log.d(TAG,"Bluetooth service connected"); + Log.d(TAG, "Bluetooth service connected, profile:" + profile); mService = (BluetoothMapClient) proxy; // We just bound to the service, so refresh the UI for any connected MAP devices. List<BluetoothDevice> deviceList = mService.getConnectedDevices(); @@ -82,14 +81,14 @@ public final class MapClientProfile implements LocalBluetoothProfile { } public void onServiceDisconnected(int profile) { - if (V) Log.d(TAG,"Bluetooth service disconnected"); + Log.d(TAG, "Bluetooth service disconnected, profile:" + profile); mProfileManager.callServiceDisconnectedListeners(); mIsProfileReady=false; } } public boolean isProfileReady() { - if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady); + Log.d(TAG, "isProfileReady(): "+ mIsProfileReady); return mIsProfileReady; } @@ -115,18 +114,16 @@ public final class MapClientProfile implements LocalBluetoothProfile { } public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - List<BluetoothDevice> connectedDevices = getConnectedDevices(); - if (connectedDevices != null && connectedDevices.contains(device)) { - // Connect to same device, Ignore it - Log.d(TAG,"Ignoring Connect"); - return true; + if (mService == null) { + return false; } return mService.connect(device); } public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; + if (mService == null) { + return false; + } // Downgrade priority as user is disconnecting. if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { mService.setPriority(device, BluetoothProfile.PRIORITY_ON); @@ -135,23 +132,30 @@ public final class MapClientProfile implements LocalBluetoothProfile { } public int getConnectionStatus(BluetoothDevice device) { - if (mService == null) return BluetoothProfile.STATE_DISCONNECTED; - + if (mService == null) { + return BluetoothProfile.STATE_DISCONNECTED; + } return mService.getConnectionState(device); } public boolean isPreferred(BluetoothDevice device) { - if (mService == null) return false; + if (mService == null) { + return false; + } return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; } public int getPreferred(BluetoothDevice device) { - if (mService == null) return BluetoothProfile.PRIORITY_OFF; + if (mService == null) { + return BluetoothProfile.PRIORITY_OFF; + } return mService.getPriority(device); } public void setPreferred(BluetoothDevice device, boolean preferred) { - if (mService == null) return; + if (mService == null) { + return; + } if (preferred) { if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) { mService.setPriority(device, BluetoothProfile.PRIORITY_ON); @@ -162,7 +166,9 @@ public final class MapClientProfile implements LocalBluetoothProfile { } public List<BluetoothDevice> getConnectedDevices() { - if (mService == null) return new ArrayList<BluetoothDevice>(0); + if (mService == null) { + return new ArrayList<BluetoothDevice>(0); + } return mService.getDevicesMatchingConnectionStates( new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING, @@ -200,11 +206,11 @@ public final class MapClientProfile implements LocalBluetoothProfile { } protected void finalize() { - if (V) Log.d(TAG, "finalize()"); + Log.d(TAG, "finalize()"); if (mService != null) { try { BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP_CLIENT, - mService); + mService); mService = null; }catch (Throwable t) { Log.w(TAG, "Error cleaning up MAP Client proxy", t); diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java new file mode 100644 index 000000000000..a070b2a5f768 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018 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.net; + +import android.app.usage.NetworkStatsManager; +import android.app.usage.NetworkStats; +import android.content.Context; +import android.os.RemoteException; +import android.telephony.TelephonyManager; +import android.util.Log; + +import androidx.loader.content.AsyncTaskLoader; + +/** + * Loader for retrieving the network stats details for all UIDs. + */ +public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { + + private static final String TAG = "NetworkDetailLoader"; + private final NetworkStatsManager mNetworkStatsManager; + private final TelephonyManager mTelephonyManager; + private final long mStart; + private final long mEnd; + private final int mSubId; + private final int mNetworkType; + + private NetworkStatsDetailLoader(Builder builder) { + super(builder.mContext); + mStart = builder.mStart; + mEnd = builder.mEnd; + mSubId = builder.mSubId; + mNetworkType = builder.mNetworkType; + mNetworkStatsManager = (NetworkStatsManager) + builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); + mTelephonyManager = + (TelephonyManager) builder.mContext.getSystemService(Context.TELEPHONY_SERVICE); + } + + @Override + protected void onStartLoading() { + super.onStartLoading(); + forceLoad(); + } + + @Override + public NetworkStats loadInBackground() { + try { + return mNetworkStatsManager.queryDetails( + mNetworkType, mTelephonyManager.getSubscriberId(mSubId), mStart, mEnd); + } catch (RemoteException e) { + Log.e(TAG, "Exception querying network detail.", e); + return null; + } + } + + @Override + protected void onStopLoading() { + super.onStopLoading(); + cancelLoad(); + } + + @Override + protected void onReset() { + super.onReset(); + cancelLoad(); + } + + public static class Builder { + private final Context mContext; + private long mStart; + private long mEnd; + private int mSubId; + private int mNetworkType; + + public Builder(Context context) { + mContext = context; + } + + public Builder setStartTime(long start) { + mStart = start; + return this; + } + + public Builder setEndTime(long end) { + mEnd = end; + return this; + } + + public Builder setSubscriptionId(int subId) { + mSubId = subId; + return this; + } + + public Builder setNetworkType(int networkType) { + mNetworkType = networkType; + return this; + } + + public NetworkStatsDetailLoader build() { + return new NetworkStatsDetailLoader(this); + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java index c311337d6cf5..82bb0115c66f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java @@ -25,6 +25,12 @@ import android.os.RemoteException; import androidx.loader.content.AsyncTaskLoader; +/** + * Deprecated in favor of {@link NetworkStatsDetailLoader} + * + * @deprecated + */ +@Deprecated public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> { private static final String KEY_TEMPLATE = "template"; private static final String KEY_START = "start"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java index 8ac611fda1fc..7baded8da1d4 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java @@ -139,7 +139,7 @@ public class CachedBluetoothDeviceManagerTest { public void testGetName_validCachedDevice_nameFound() { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); assertThat(cachedDevice1).isNotNull(); - assertThat(mCachedDeviceManager.findDevice(mDevice1).getName()).isEqualTo(DEVICE_ALIAS_1); + assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1); } /** diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java new file mode 100644 index 000000000000..c91ee22d8587 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 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.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHidDevice; +import android.bluetooth.BluetoothProfile; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; + +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; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadow.api.Shadow; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) +public class HidDeviceProfileTest { + + @Mock + private CachedBluetoothDeviceManager mDeviceManager; + @Mock + private LocalBluetoothProfileManager mProfileManager; + @Mock + private BluetoothHidDevice mService; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + private BluetoothProfile.ServiceListener mServiceListener; + private HidDeviceProfile mProfile; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mProfile = new HidDeviceProfile(RuntimeEnvironment.application, + mDeviceManager, mProfileManager); + mServiceListener = mShadowBluetoothAdapter.getServiceListener(); + mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, mService); + } + + @Test + public void connect_shouldReturnFalse() { + assertThat(mProfile.connect(mBluetoothDevice)).isFalse(); + } + + @Test + public void disconnect_shouldDisconnectBluetoothHidDevice() { + mProfile.disconnect(mBluetoothDevice); + verify(mService).disconnect(mBluetoothDevice); + } + + @Test + public void getConnectionStatus_shouldReturnConnectionState() { + when(mService.getConnectionState(mBluetoothDevice)). + thenReturn(BluetoothProfile.STATE_CONNECTED); + assertThat(mProfile.getConnectionStatus(mBluetoothDevice)). + isEqualTo(BluetoothProfile.STATE_CONNECTED); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java new file mode 100644 index 000000000000..c4c48a8bce8c --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 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.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothMapClient; +import android.bluetooth.BluetoothProfile; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; + +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; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadow.api.Shadow; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) +public class MapClientProfileTest { + + @Mock + private CachedBluetoothDeviceManager mDeviceManager; + @Mock + private LocalBluetoothProfileManager mProfileManager; + @Mock + private BluetoothMapClient mService; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + private BluetoothProfile.ServiceListener mServiceListener; + private MapClientProfile mProfile; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mProfile = new MapClientProfile(RuntimeEnvironment.application, + mDeviceManager, mProfileManager); + mServiceListener = mShadowBluetoothAdapter.getServiceListener(); + mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, mService); + } + + @Test + public void connect_shouldConnectBluetoothMapClient() { + mProfile.connect(mBluetoothDevice); + verify(mService).connect(mBluetoothDevice); + } + + @Test + public void disconnect_shouldDisconnectBluetoothMapClient() { + mProfile.disconnect(mBluetoothDevice); + verify(mService).disconnect(mBluetoothDevice); + } + + @Test + public void getConnectionStatus_shouldReturnConnectionState() { + when(mService.getConnectionState(mBluetoothDevice)). + thenReturn(BluetoothProfile.STATE_CONNECTED); + assertThat(mProfile.getConnectionStatus(mBluetoothDevice)). + isEqualTo(BluetoothProfile.STATE_CONNECTED); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index cc1b9e8f9c49..bde7f1bd86ad 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -55,6 +55,7 @@ import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArraySet; +import android.util.FeatureFlagUtils; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -318,8 +319,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, ArraySet<String> addedKeys = new ArraySet<String>(); mHasLogoutButton = false; mHasLockdownButton = false; - mSeparatedEmergencyButtonEnabled = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0; + mSeparatedEmergencyButtonEnabled = FeatureFlagUtils + .isEnabled(mContext, FeatureFlagUtils.EMERGENCY_DIAL_SHORTCUTS); for (int i = 0; i < defaultActions.length; i++) { String actionKey = defaultActions[i]; if (addedKeys.contains(actionKey)) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index ca1b48901c9d..123fca71deaf 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -391,6 +391,9 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { position = mPages.size() - 1 - position; } ViewGroup view = mPages.get(position); + if (view.getParent() != null) { + container.removeView(view); + } container.addView(view); updateListening(); return view; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 167bba60b390..51a5df2730ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -135,6 +135,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat mWindowManager.addView(mStatusBarView, mLp); mLpChanged = new WindowManager.LayoutParams(); mLpChanged.copyFrom(mLp); + onThemeChanged(); } public void setDozeScreenBrightness(int value) { @@ -483,6 +484,10 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat @Override public void onThemeChanged() { + if (mStatusBarView == null) { + return; + } + StatusBarStateController state = Dependency.get(StatusBarStateController.class); int which; if (state.getState() == StatusBarState.KEYGUARD diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java index f8223f61edb4..f7a7e0430977 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -83,4 +84,16 @@ public class StatusBarWindowControllerTest extends SysuiTestCase { & WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; assertThat(flag).isEqualTo(0); } + + @Test + public void testOnThemeChanged_doesntCrash() { + mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager, + mActivityManager, mDozeParameters); + mStatusBarWindowController.onThemeChanged(); + } + + @Test + public void testAdd_updatesVisibilityFlags() { + verify(mStatusBarView).setSystemUiVisibility(anyInt()); + } } diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index 1866420f2a81..01421c7e65c2 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -126,11 +126,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); - @VisibleForTesting - ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) { - this(supervisor, supervisor.mDisplayManager.getDisplay(displayId)); - } - ActivityDisplay(ActivityStackSupervisor supervisor, Display display) { mSupervisor = supervisor; mDisplayId = display.getDisplayId(); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index eb41fe7323ac..bcb2b9b9ddfd 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -52,6 +52,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; + import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; @@ -1901,14 +1902,20 @@ class ActivityStarter { // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have // the same behavior as if a new instance was being started, which means not bringing it // to the front if the caller is not itself in the front. - final ActivityStack focusStack = mSupervisor.getTopDisplayFocusedStack(); - ActivityRecord curTop = (focusStack == null) - ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); - - final TaskRecord topTask = curTop != null ? curTop.getTask() : null; - if (topTask != null - && (topTask != intentActivity.getTask() || topTask != focusStack.topTask()) - && !mAvoidMoveToFront) { + final boolean differentTopTask; + if (mPreferredDisplayId == mTargetStack.mDisplayId) { + final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack(); + final ActivityRecord curTop = (focusStack == null) + ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); + final TaskRecord topTask = curTop != null ? curTop.getTask() : null; + differentTopTask = topTask != null + && (topTask != intentActivity.getTask() || topTask != focusStack.topTask()); + } else { + // The existing task should always be different from those in other displays. + differentTopTask = true; + } + + if (differentTopTask && !mAvoidMoveToFront) { mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); if (mSourceRecord == null || (mSourceStack.getTopActivity() != null && mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2e2a3c1750d3..33f0172811ea 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4402,22 +4402,12 @@ public class NotificationManagerService extends SystemService { } protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) { - final String pkg = r.sbn.getPackageName(); - final int callingUid = r.sbn.getUid(); - - final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid); - if (isPackageSuspended) { - Slog.e(TAG, "Suppressing notification from package due to package " - + "suspended by administrator."); - usageStats.registerSuspendedByAdmin(r); - return isPackageSuspended; - } - final boolean isBlocked = isBlocked(r); - if (isBlocked) { + if (isBlocked(r)) { Slog.e(TAG, "Suppressing notification from package by user request."); usageStats.registerBlocked(r); + return true; } - return isBlocked; + return false; } private boolean isBlocked(NotificationRecord r) { @@ -4691,7 +4681,11 @@ public class NotificationManagerService extends SystemService { return; } - r.setHidden(isPackageSuspendedLocked(r)); + final boolean isPackageSuspended = isPackageSuspendedLocked(r); + r.setHidden(isPackageSuspended); + if (isPackageSuspended) { + mUsageStats.registerSuspendedByAdmin(r); + } NotificationRecord old = mNotificationsByKey.get(key); final StatusBarNotification n = r.sbn; final Notification notification = n.getNotification(); @@ -6922,7 +6916,6 @@ public class NotificationManagerService extends SystemService { if (!oldSbnVisible && !sbnVisible) { continue; } - // If the notification is hidden, don't notifyPosted listeners targeting < P. // Instead, those listeners will receive notifyPosted when the notification is // unhidden. @@ -7356,7 +7349,7 @@ public class NotificationManagerService extends SystemService { new String[]{pkg}); final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED - : Intent.ACTION_PACKAGES_UNSUSPENDED; + : Intent.ACTION_PACKAGES_UNSUSPENDED; final Intent intent = new Intent(action); intent.putExtras(extras); diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java index 005374571627..5bf323ac2239 100644 --- a/services/core/java/com/android/server/pm/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/ComponentResolver.java @@ -1048,11 +1048,14 @@ public class ComponentResolver { final String otherPackageName = (other != null && other.getComponentName() != null) ? other.getComponentName().getPackageName() : "?"; - throw new PackageManagerException( - INSTALL_FAILED_CONFLICTING_PROVIDER, - "Can't install because provider name " + names[j] - + " (in package " + pkg.applicationInfo.packageName - + ") is already used by " + otherPackageName); + // if we're installing over the same already-installed package, this is ok + if (otherPackageName != pkg.packageName) { + throw new PackageManagerException( + INSTALL_FAILED_CONFLICTING_PROVIDER, + "Can't install because provider name " + names[j] + + " (in package " + pkg.applicationInfo.packageName + + ") is already used by " + otherPackageName); + } } } } diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index bca3ca71a911..5c6fe16772c8 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -63,6 +63,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { private final Context mContext; private final PackageManagerService mPackageManagerService; + private final MetricsLogger metricsLogger; // TODO: Evaluate the need for WeakReferences here. @@ -95,6 +96,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { public OtaDexoptService(Context context, PackageManagerService packageManagerService) { this.mContext = context; this.mPackageManagerService = packageManagerService; + metricsLogger = new MetricsLogger(); } public static OtaDexoptService main(Context context, @@ -445,24 +447,22 @@ public class OtaDexoptService extends IOtaDexopt.Stub { private void performMetricsLogging() { long finalTime = System.nanoTime(); - MetricsLogger.histogram(mContext, "ota_dexopt_available_space_before_mb", + metricsLogger.histogram("ota_dexopt_available_space_before_mb", inMegabytes(availableSpaceBefore)); - MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_bulk_delete_mb", + metricsLogger.histogram("ota_dexopt_available_space_after_bulk_delete_mb", inMegabytes(availableSpaceAfterBulkDelete)); - MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_dexopt_mb", + metricsLogger.histogram("ota_dexopt_available_space_after_dexopt_mb", inMegabytes(availableSpaceAfterDexopt)); - MetricsLogger.histogram(mContext, "ota_dexopt_num_important_packages", - importantPackageCount); - MetricsLogger.histogram(mContext, "ota_dexopt_num_other_packages", otherPackageCount); + metricsLogger.histogram("ota_dexopt_num_important_packages", importantPackageCount); + metricsLogger.histogram("ota_dexopt_num_other_packages", otherPackageCount); - MetricsLogger.histogram(mContext, "ota_dexopt_num_commands", dexoptCommandCountTotal); - MetricsLogger.histogram(mContext, "ota_dexopt_num_commands_executed", - dexoptCommandCountExecuted); + metricsLogger.histogram("ota_dexopt_num_commands", dexoptCommandCountTotal); + metricsLogger.histogram("ota_dexopt_num_commands_executed", dexoptCommandCountExecuted); final int elapsedTimeSeconds = (int) TimeUnit.NANOSECONDS.toSeconds(finalTime - otaDexoptTimeStart); - MetricsLogger.histogram(mContext, "ota_dexopt_time_s", elapsedTimeSeconds); + metricsLogger.histogram("ota_dexopt_time_s", elapsedTimeSeconds); } private static class OTADexoptPackageDexOptimizer extends diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index db7a109dee15..91af0eca8707 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -111,7 +111,8 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures; 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 com.android.server.pm.permission.PermissionsState + .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; import android.Manifest; import android.annotation.IntDef; @@ -303,7 +304,8 @@ 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.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; +import com.android.server.pm.permission.DefaultPermissionGrantPolicy + .DefaultPermissionGrantedCallback; import com.android.server.pm.permission.PermissionManagerInternal; import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionManagerService; @@ -8584,7 +8586,10 @@ public class PackageManagerService extends IPackageManager.Stub null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); - scanPackageOnlyLI(request, mFactoryTest, -1L); + final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); + if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { + scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); + } } } } @@ -9807,6 +9812,11 @@ public class PackageManagerService extends IPackageManager.Stub /** Whether or not the package scan was successful */ public final boolean success; /** + * Whether or not the original PackageSetting needs to be updated with this result on + * commit. + */ + public final boolean existingSettingCopied; + /** * The final package settings. This may be the same object passed in * the {@link ScanRequest}, but, with modified values. */ @@ -9816,11 +9826,12 @@ public class PackageManagerService extends IPackageManager.Stub public ScanResult( ScanRequest request, boolean success, @Nullable PackageSetting pkgSetting, - @Nullable List<String> changedAbiCodePath) { + @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied) { this.request = request; this.success = success; this.pkgSetting = pkgSetting; this.changedAbiCodePath = changedAbiCodePath; + this.existingSettingCopied = existingSettingCopied; } } @@ -9897,26 +9908,34 @@ public class PackageManagerService extends IPackageManager.Stub private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags, PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user, PackageParser.Package pkg) { - if (disabledPkgSetting != null) { + + // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain + // the correct isSystem value now that we don't disable system packages before scan. + final PackageSetting systemPkgSetting = + (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null + && pkgSetting != null && pkgSetting.isSystem() + ? pkgSetting + : disabledPkgSetting; + if (systemPkgSetting != null) { // updated system application, must at least have SCAN_AS_SYSTEM scanFlags |= SCAN_AS_SYSTEM; - if ((disabledPkgSetting.pkgPrivateFlags + if ((systemPkgSetting.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { scanFlags |= SCAN_AS_PRIVILEGED; } - if ((disabledPkgSetting.pkgPrivateFlags + if ((systemPkgSetting.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) { scanFlags |= SCAN_AS_OEM; } - if ((disabledPkgSetting.pkgPrivateFlags + if ((systemPkgSetting.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) { scanFlags |= SCAN_AS_VENDOR; } - if ((disabledPkgSetting.pkgPrivateFlags + if ((systemPkgSetting.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) { scanFlags |= SCAN_AS_PRODUCT; } - if ((disabledPkgSetting.pkgPrivateFlags + if ((systemPkgSetting.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0) { scanFlags |= SCAN_AS_PRODUCT_SERVICES; } @@ -10046,11 +10065,18 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting disabledPkgSetting = request.disabledPkgSetting; final UserHandle user = request.user; final String realPkgName = request.realPkgName; - final PackageSetting pkgSetting = result.pkgSetting; final List<String> changedAbiCodePath = result.changedAbiCodePath; - final boolean newPkgSettingCreated = (result.pkgSetting != request.pkgSetting); - - if (newPkgSettingCreated) { + final PackageSetting pkgSetting; + if (result.existingSettingCopied) { + pkgSetting = request.pkgSetting; + pkgSetting.updateFrom(result.pkgSetting); + pkg.mExtras = pkgSetting; + if (pkgSetting.sharedUser != null + && pkgSetting.sharedUser.removePackage(result.pkgSetting)) { + pkgSetting.sharedUser.addPackage(pkgSetting); + } + } else { + pkgSetting = result.pkgSetting; if (originalPkgSetting != null) { mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name); } @@ -10357,7 +10383,6 @@ public class PackageManagerService extends IPackageManager.Stub String primaryCpuAbiFromSettings = null; String secondaryCpuAbiFromSettings = null; boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0; - if (!needToDeriveAbi) { if (pkgSetting != null) { primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString; @@ -10401,6 +10426,11 @@ public class PackageManagerService extends IPackageManager.Stub UserManagerService.getInstance(), usesStaticLibraries, pkg.usesStaticLibrariesVersions); } else { + if (!createNewPackage) { + // make a deep copy to avoid modifying any existing system state. + pkgSetting = new PackageSetting(pkgSetting); + pkgSetting.pkg = pkg; + } // REMOVE SharedUserSetting from method; update in a separate call. // // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi, @@ -10433,8 +10463,10 @@ public class PackageManagerService extends IPackageManager.Stub final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0; setInstantAppForUser(pkgSetting, userId, instantApp, fullApp); } - - if (disabledPkgSetting != null) { + // TODO(patb): see if we can do away with disabled check here. + if (disabledPkgSetting != null + || (0 != (scanFlags & SCAN_NEW_INSTALL) + && pkgSetting != null && pkgSetting.isSystem())) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } @@ -10617,7 +10649,8 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting.volumeUuid = volumeUuid; } - return new ScanResult(request, true, pkgSetting, changedAbiCodePath); + return new ScanResult(request, true, pkgSetting, changedAbiCodePath, + !createNewPackage /* existingSettingCopied */); } /** @@ -10832,7 +10865,7 @@ public class PackageManagerService extends IPackageManager.Stub } // A package name must be unique; don't allow duplicates - if (mPackages.containsKey(pkg.packageName)) { + if ((scanFlags & SCAN_NEW_INSTALL) == 0 && mPackages.containsKey(pkg.packageName)) { throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, "Application package " + pkg.packageName + " already installed. Skipping duplicate."); @@ -10841,7 +10874,8 @@ public class PackageManagerService extends IPackageManager.Stub if (pkg.applicationInfo.isStaticSharedLibrary()) { // Static libs have a synthetic package name containing the version // but we still want the base name to be unique. - if (mPackages.containsKey(pkg.manifestPackageName)) { + if ((scanFlags & SCAN_NEW_INSTALL) == 0 + && mPackages.containsKey(pkg.manifestPackageName)) { throw new PackageManagerException( "Duplicate static shared lib provider package"); } @@ -11930,7 +11964,9 @@ public class PackageManagerService extends IPackageManager.Stub // Remove the parent package setting PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps != null) { - removePackageLI(ps, chatty); + removePackageLI(ps.name, chatty); + } else if (DEBUG_REMOVE && chatty) { + Log.d(TAG, "Not removing package " + pkg.packageName + "; mExtras == null"); } // Remove the child package setting final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; @@ -11938,23 +11974,22 @@ public class PackageManagerService extends IPackageManager.Stub PackageParser.Package childPkg = pkg.childPackages.get(i); ps = (PackageSetting) childPkg.mExtras; if (ps != null) { - removePackageLI(ps, chatty); + removePackageLI(ps.name, chatty); } } } - void removePackageLI(PackageSetting ps, boolean chatty) { + void removePackageLI(String packageName, boolean chatty) { if (DEBUG_INSTALL) { if (chatty) - Log.d(TAG, "Removing package " + ps.name); + Log.d(TAG, "Removing package " + packageName); } // writer synchronized (mPackages) { - mPackages.remove(ps.name); - final PackageParser.Package pkg = ps.pkg; - if (pkg != null) { - cleanPackageDataStructuresLILPw(pkg, chatty); + final PackageParser.Package removedPackage = mPackages.remove(packageName); + if (removedPackage != null) { + cleanPackageDataStructuresLILPw(removedPackage, chatty); } } } @@ -14655,68 +14690,6 @@ public class PackageManagerService extends IPackageManager.Stub String origPermission; } - /* - * Install a non-existing package. - */ - private void installNewPackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags, - final @ScanFlags int scanFlags, UserHandle user, String installerPackageName, - String volumeUuid, PackageInstalledInfo res, int installReason) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage"); - - // Remember this for later, in case we need to rollback this install - String pkgName = pkg.packageName; - - if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); - - synchronized(mPackages) { - final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName); - if (renamedPackage != null) { - // A package with the same name is already installed, though - // it has been renamed to an older name. The package we - // are trying to install should be installed as an update to - // the existing one, but that has not been requested, so bail. - res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName - + " without first uninstalling package running as " - + renamedPackage); - return; - } - if (mPackages.containsKey(pkgName)) { - // Don't allow installation over an existing package with the same name. - res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName - + " without first uninstalling."); - return; - } - } - - try { - final PackageParser.Package newPackage; - List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags, scanFlags, - System.currentTimeMillis(), user); - commitSuccessfulScanResults(scanResults); - // TODO(b/109941548): Child packages may return >1 result with the first being the base; - // we need to treat child packages as an atomic install and remove this hack - final ScanResult basePackageScanResult = scanResults.get(0); - newPackage = basePackageScanResult.pkgSetting.pkg; - updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason); - - if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - prepareAppDataAfterInstallLIF(newPackage); - } else { - // Remove package from internal structures, but keep around any - // data that might have already existed - deletePackageLIF(pkgName, UserHandle.ALL, false, null, - PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null); - } - } catch (PackageManagerException e) { - destroyAppDataLIF(pkg, UserHandle.USER_ALL, - StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); - destroyAppProfilesLIF(pkg, UserHandle.USER_ALL); - res.setError("Package couldn't be installed in " + pkg.codePath, e); - } - - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - private static void updateDigest(MessageDigest digest, File file) throws IOException { try (DigestInputStream digestStream = new DigestInputStream(new FileInputStream(file), digest)) { @@ -14724,482 +14697,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private void replacePackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags, - final @ScanFlags int scanFlags, UserHandle user, String installerPackageName, - PackageInstalledInfo res, int installReason) { - final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; - - final PackageParser.Package oldPackage; - final PackageSetting ps; - final String pkgName = pkg.packageName; - final int[] allUsers; - final int[] installedUsers; - - synchronized(mPackages) { - oldPackage = mPackages.get(pkgName); - if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage); - - // don't allow upgrade to target a release SDK from a pre-release SDK - final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion - == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; - final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion - == android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; - if (oldTargetsPreRelease - && !newTargetsPreRelease - && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) { - Slog.w(TAG, "Can't install package targeting released sdk"); - res.setReturnCode(PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); - return; - } - - ps = mSettings.mPackages.get(pkgName); - - // verify signatures are valid - final KeySetManagerService ksms = mSettings.mKeySetManagerService; - if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) { - if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) { - res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "New package not signed by keys specified by upgrade-keysets: " - + pkgName); - return; - } - } else { - - // default to original signature matching - if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails, - PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA) - && !oldPackage.mSigningDetails.checkCapability( - pkg.mSigningDetails, - PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) { - res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "New package has a different signature: " + pkgName); - return; - } - } - - // don't allow a system upgrade unless the upgrade hash matches - if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) { - final byte[] digestBytes; - try { - final MessageDigest digest = MessageDigest.getInstance("SHA-512"); - updateDigest(digest, new File(pkg.baseCodePath)); - if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { - for (String path : pkg.splitCodePaths) { - updateDigest(digest, new File(path)); - } - } - digestBytes = digest.digest(); - } catch (NoSuchAlgorithmException | IOException e) { - res.setError(INSTALL_FAILED_INVALID_APK, - "Could not compute hash: " + pkgName); - return; - } - if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) { - res.setError(INSTALL_FAILED_INVALID_APK, - "New package fails restrict-update check: " + pkgName); - return; - } - // retain upgrade restriction - pkg.restrictUpdateHash = oldPackage.restrictUpdateHash; - } - - // Check for shared user id changes - String invalidPackageName = - getParentOrChildPackageChangedSharedUser(oldPackage, pkg); - if (invalidPackageName != null) { - res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, - "Package " + invalidPackageName + " tried to change user " - + oldPackage.mSharedUserId); - return; - } - - // check if the new package supports all of the abis which the old package supports - boolean oldPkgSupportMultiArch = oldPackage.applicationInfo.secondaryCpuAbi != null; - boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null; - if (isSystemApp(oldPackage) && oldPkgSupportMultiArch && !newPkgSupportMultiArch) { - res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, - "Update to package " + pkgName + " doesn't support multi arch"); - return; - } - - // In case of rollback, remember per-user/profile install state - allUsers = sUserManager.getUserIds(); - installedUsers = ps.queryInstalledUsers(allUsers, true); - - // don't allow an upgrade from full to ephemeral - if (isInstantApp) { - if (user == null || user.getIdentifier() == UserHandle.USER_ALL) { - for (int currentUser : allUsers) { - if (!ps.getInstantApp(currentUser)) { - // can't downgrade from full to instant - Slog.w(TAG, "Can't replace full app with instant app: " + pkgName - + " for user: " + currentUser); - res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID); - return; - } - } - } else if (!ps.getInstantApp(user.getIdentifier())) { - // can't downgrade from full to instant - Slog.w(TAG, "Can't replace full app with instant app: " + pkgName - + " for user: " + user.getIdentifier()); - res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID); - return; - } - } - } - - // Update what is removed - res.removedInfo = new PackageRemovedInfo(this); - res.removedInfo.uid = oldPackage.applicationInfo.uid; - res.removedInfo.removedPackage = oldPackage.packageName; - res.removedInfo.installerPackageName = ps.installerPackageName; - res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null; - res.removedInfo.isUpdate = true; - res.removedInfo.origUsers = installedUsers; - res.removedInfo.installReasons = new SparseArray<>(installedUsers.length); - for (int i = 0; i < installedUsers.length; i++) { - final int userId = installedUsers[i]; - res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId)); - } - - final int childCount = (oldPackage.childPackages != null) - ? oldPackage.childPackages.size() : 0; - for (int i = 0; i < childCount; i++) { - boolean childPackageUpdated = false; - PackageParser.Package childPkg = oldPackage.childPackages.get(i); - final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName); - if (res.addedChildPackages != null) { - PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName); - if (childRes != null) { - childRes.removedInfo.uid = childPkg.applicationInfo.uid; - childRes.removedInfo.removedPackage = childPkg.packageName; - if (childPs != null) { - childRes.removedInfo.installerPackageName = childPs.installerPackageName; - } - childRes.removedInfo.isUpdate = true; - childRes.removedInfo.installReasons = res.removedInfo.installReasons; - childPackageUpdated = true; - } - } - if (!childPackageUpdated) { - PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this); - childRemovedRes.removedPackage = childPkg.packageName; - if (childPs != null) { - childRemovedRes.installerPackageName = childPs.installerPackageName; - } - childRemovedRes.isUpdate = false; - childRemovedRes.dataRemoved = true; - synchronized (mPackages) { - if (childPs != null) { - childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, true); - } - } - if (res.removedInfo.removedChildPackages == null) { - res.removedInfo.removedChildPackages = new ArrayMap<>(); - } - res.removedInfo.removedChildPackages.put(childPkg.packageName, childRemovedRes); - } - } - - boolean sysPkg = (isSystemApp(oldPackage)); - if (sysPkg) { - // Set the system/privileged/oem/vendor/product flags as needed - final boolean privileged = isPrivilegedApp(oldPackage); - final boolean oem = isOemApp(oldPackage); - final boolean vendor = isVendorApp(oldPackage); - final boolean product = isProductApp(oldPackage); - final boolean productServices = isProductServicesApp(oldPackage); - - final @ParseFlags int systemParseFlags = parseFlags; - final @ScanFlags int systemScanFlags = scanFlags - | SCAN_AS_SYSTEM - | (privileged ? SCAN_AS_PRIVILEGED : 0) - | (oem ? SCAN_AS_OEM : 0) - | (vendor ? SCAN_AS_VENDOR : 0) - | (product ? SCAN_AS_PRODUCT : 0) - | (productServices ? SCAN_AS_PRODUCT_SERVICES : 0); - - replaceSystemPackageLIF(oldPackage, pkg, systemParseFlags, systemScanFlags, - user, allUsers, installerPackageName, res, installReason); - } else { - replaceNonSystemPackageLIF(oldPackage, pkg, parseFlags, scanFlags, - user, allUsers, installerPackageName, res, installReason); - } - } - - private void replaceNonSystemPackageLIF(PackageParser.Package deletedPackage, - PackageParser.Package pkg, final @ParseFlags int parseFlags, - final @ScanFlags int scanFlags, UserHandle user, int[] allUsers, - String installerPackageName, PackageInstalledInfo res, int installReason) { - if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old=" - + deletedPackage); - - String pkgName = deletedPackage.packageName; - boolean deletedPkg = true; - boolean addedPkg = false; - boolean updatedSettings = false; - final boolean killApp = (scanFlags & SCAN_DONT_KILL_APP) == 0; - final int deleteFlags = PackageManager.DELETE_KEEP_DATA - | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP); - - final long origUpdateTime = (pkg.mExtras != null) - ? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0; - - // First delete the existing package while retaining the data directory - if (!deletePackageLIF(pkgName, null, true, allUsers, deleteFlags, - res.removedInfo, true, pkg)) { - // If the existing package wasn't successfully deleted - res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI"); - deletedPkg = false; - } else { - // Successfully deleted the old package; proceed with replace. - - // If deleted package lived in a container, give users a chance to - // relinquish resources before killing. - if (deletedPackage.isForwardLocked() || isExternal(deletedPackage)) { - if (DEBUG_INSTALL) { - Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE"); - } - final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid }; - final ArrayList<String> pkgList = new ArrayList<>(1); - pkgList.add(deletedPackage.applicationInfo.packageName); - sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null); - } - - clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE - | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); - - try { - final List<ScanResult> scanResults = scanPackageTracedLI(pkg, parseFlags, - scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user); - commitSuccessfulScanResults(scanResults); - final PackageParser.Package newPackage = scanResults.get(0).pkgSetting.pkg; - updateSettingsLI(newPackage, installerPackageName, allUsers, res, user, - installReason); - - // Update the in-memory copy of the previous code paths. - PackageSetting ps = mSettings.mPackages.get(pkgName); - if (!killApp) { - if (ps.oldCodePaths == null) { - ps.oldCodePaths = new ArraySet<>(); - } - Collections.addAll(ps.oldCodePaths, deletedPackage.baseCodePath); - if (deletedPackage.splitCodePaths != null) { - Collections.addAll(ps.oldCodePaths, deletedPackage.splitCodePaths); - } - } else { - ps.oldCodePaths = null; - } - if (ps.childPackageNames != null) { - for (int i = ps.childPackageNames.size() - 1; i >= 0; --i) { - final String childPkgName = ps.childPackageNames.get(i); - final PackageSetting childPs = mSettings.mPackages.get(childPkgName); - childPs.oldCodePaths = ps.oldCodePaths; - } - } - prepareAppDataAfterInstallLIF(newPackage); - addedPkg = true; - mDexManager.notifyPackageUpdated(newPackage.packageName, - newPackage.baseCodePath, newPackage.splitCodePaths); - } catch (PackageManagerException e) { - res.setError("Package couldn't be installed in " + pkg.codePath, e); - } - } - - if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { - if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName); - - // Revert all internal state mutations and added folders for the failed install - if (addedPkg) { - deletePackageLIF(pkgName, null, true, allUsers, deleteFlags, - res.removedInfo, true, null); - } - - // Restore the old package - if (deletedPkg) { - if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage); - File restoreFile = new File(deletedPackage.codePath); - // Parse old package - boolean oldExternal = isExternal(deletedPackage); - int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | - (deletedPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0) | - (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0); - int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME; - try { - scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, - null); - } catch (PackageManagerException e) { - Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: " - + e.getMessage()); - return; - } - - synchronized (mPackages) { - // Ensure the installer package name up to date - setInstallerPackageNameLPw(deletedPackage, installerPackageName); - - // Update permissions for restored package - mPermissionManager.updatePermissions( - deletedPackage.packageName, deletedPackage, false, mPackages.values(), - mPermissionCallback); - - mSettings.writeLPr(); - } - - Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade"); - } - } else { - synchronized (mPackages) { - PackageSetting ps = mSettings.getPackageLPr(pkg.packageName); - if (ps != null) { - res.removedInfo.removedForAllUsers = mPackages.get(ps.name) == null; - if (res.removedInfo.removedChildPackages != null) { - final int childCount = res.removedInfo.removedChildPackages.size(); - // Iterate in reverse as we may modify the collection - for (int i = childCount - 1; i >= 0; i--) { - String childPackageName = res.removedInfo.removedChildPackages.keyAt(i); - if (res.addedChildPackages.containsKey(childPackageName)) { - res.removedInfo.removedChildPackages.removeAt(i); - } else { - PackageRemovedInfo childInfo = res.removedInfo - .removedChildPackages.valueAt(i); - childInfo.removedForAllUsers = mPackages.get( - childInfo.removedPackage) == null; - } - } - } - } - } - } - } - - private void replaceSystemPackageLIF(PackageParser.Package deletedPackage, - PackageParser.Package pkg, final @ParseFlags int parseFlags, - final @ScanFlags int scanFlags, UserHandle user, - int[] allUsers, String installerPackageName, PackageInstalledInfo res, - int installReason) { - if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg - + ", old=" + deletedPackage); - - final boolean disabledSystem; - - // Remove existing system package - removePackageLI(deletedPackage, true); - - synchronized (mPackages) { - disabledSystem = disableSystemPackageLPw(deletedPackage, pkg); - } - if (!disabledSystem) { - // We didn't need to disable the .apk as a current system package, - // which means we are replacing another update that is already - // installed. We need to make sure to delete the older one's .apk. - res.removedInfo.args = createInstallArgsForExisting(0, - deletedPackage.applicationInfo.getCodePath(), - deletedPackage.applicationInfo.getResourcePath(), - getAppDexInstructionSets(deletedPackage.applicationInfo)); - } else { - res.removedInfo.args = null; - } - - // Successfully disabled the old package. Now proceed with re-installation - clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE - | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); - - res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); - pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, - ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); - - PackageParser.Package newPackage = null; - try { - final List<ScanResult> scanResults = - scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user); - // Add the package to the internal data structures - commitSuccessfulScanResults(scanResults); - newPackage = scanResults.get(0).pkgSetting.pkg; - - // Set the update and install times - PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras; - setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime, - System.currentTimeMillis()); - - // Update the package dynamic state if succeeded - if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - // Now that the install succeeded make sure we remove data - // directories for any child package the update removed. - final int deletedChildCount = (deletedPackage.childPackages != null) - ? deletedPackage.childPackages.size() : 0; - final int newChildCount = (newPackage.childPackages != null) - ? newPackage.childPackages.size() : 0; - for (int i = 0; i < deletedChildCount; i++) { - PackageParser.Package deletedChildPkg = deletedPackage.childPackages.get(i); - boolean childPackageDeleted = true; - for (int j = 0; j < newChildCount; j++) { - PackageParser.Package newChildPkg = newPackage.childPackages.get(j); - if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) { - childPackageDeleted = false; - break; - } - } - if (childPackageDeleted) { - PackageSetting ps = mSettings.getDisabledSystemPkgLPr( - deletedChildPkg.packageName); - if (ps != null && res.removedInfo.removedChildPackages != null) { - PackageRemovedInfo removedChildRes = res.removedInfo - .removedChildPackages.get(deletedChildPkg.packageName); - removePackageDataLIF(ps, allUsers, removedChildRes, 0, false); - removedChildRes.removedForAllUsers = mPackages.get(ps.name) == null; - } - } - } - - updateSettingsLI(newPackage, installerPackageName, allUsers, res, user, - installReason); - prepareAppDataAfterInstallLIF(newPackage); - - mDexManager.notifyPackageUpdated(newPackage.packageName, - newPackage.baseCodePath, newPackage.splitCodePaths); - } - } catch (PackageManagerException e) { - res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR); - res.setError("Package couldn't be installed in " + pkg.codePath, e); - } - - if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { - // Re installation failed. Restore old information - // Remove new pkg information - if (newPackage != null) { - removeInstalledPackageLI(newPackage, true); - } - // Add back the old system package - try { - final List<ScanResult> scanResults = scanPackageTracedLI( - deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user); - commitSuccessfulScanResults(scanResults); - } catch (PackageManagerException e) { - Slog.e(TAG, "Failed to restore original package: " + e.getMessage()); - } - - synchronized (mPackages) { - if (disabledSystem) { - enableSystemPackageLPw(deletedPackage); - } - - // Ensure the installer package name up to date - setInstallerPackageNameLPw(deletedPackage, installerPackageName); - - // Update permissions for restored package - mPermissionManager.updatePermissions( - deletedPackage.packageName, deletedPackage, false, mPackages.values(), - mPermissionCallback); - - mSettings.writeLPr(); - } - - Slog.i(TAG, "Successfully restored package : " + deletedPackage.packageName - + " after failed upgrade"); - } - } - /** * Checks whether the parent or any of the child packages have a change shared * user. For a package to be a valid update the shred users of the parent and @@ -15396,92 +14893,683 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } + private static class InstallRequest { - final InstallArgs args; - final PackageInstalledInfo res; + public final InstallArgs args; + public final PackageInstalledInfo installResult; private InstallRequest(InstallArgs args, PackageInstalledInfo res) { this.args = args; - this.res = res; + this.installResult = res; } } @GuardedBy({"mInstallLock", "mPackages"}) - private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) { + private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo installResult) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage"); - installPackagesLI(Collections.singletonList(new InstallRequest(args, res))); + installPackagesLI(Collections.singletonList(new InstallRequest(args, installResult))); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } + /** + * Package state to commit to memory and disk after reconciliation has completed. + */ private static class CommitRequest { final Map<String, ReconciledPackage> reconciledPackages; + final int[] mAllUsers; - private CommitRequest(Map<String, ReconciledPackage> reconciledPackages) { + private CommitRequest(Map<String, ReconciledPackage> reconciledPackages, int[] allUsers) { this.reconciledPackages = reconciledPackages; + this.mAllUsers = allUsers; } } - private static class ReconcileRequest { - final Map<String, ScanResult> scannedPackages; - private ReconcileRequest(Map<String, ScanResult> scannedPackages) { + /** + * Package scan results and related request details used to reconcile the potential addition of + * one or more packages to the system. + * + * Reconcile will take a set of package details that need to be committed to the system and make + * sure that they are valid in the context of the system and the other installing apps. Any + * invalid state or app will result in a failed reconciliation and thus whatever operation (such + * as install) led to the request. + */ + private static class ReconcileRequest { + public final Map<String, ScanResult> scannedPackages; + + // TODO: Remove install-specific details from reconcile request; make them generic types + // that can be used for scanDir for example. + public final Map<String, InstallArgs> installArgs; + public final Map<String, PackageInstalledInfo> installResults; + public final Map<String, PrepareResult> preparedPackages; + + private ReconcileRequest(Map<String, ScanResult> scannedPackages, + Map<String, InstallArgs> installArgs, + Map<String, PackageInstalledInfo> installResults, + Map<String, PrepareResult> preparedPackages) { this.scannedPackages = scannedPackages; + this.installArgs = installArgs; + this.installResults = installResults; + this.preparedPackages = preparedPackages; } } private static class ReconcileFailure extends PackageManagerException { public ReconcileFailure(String message) { super("Invalid reconcile request: " + message); } - }; + } /** * A container of all data needed to commit a package to in-memory data structures and to disk. - * Ideally most of the data contained in this class will move into a PackageSetting it contains. + * TODO: move most of the data contained her into a PackageSetting for commit. */ - private static class ReconciledPackage {} + private static class ReconciledPackage { + public final PackageSetting pkgSetting; + public final ScanResult scanResult; + public final UserHandle installForUser; + public final String volumeUuid; + // TODO: Remove install-specific details from the reconcile result + public final PackageInstalledInfo installResult; + public final PrepareResult prepareResult; + @PackageManager.InstallFlags + public final int installFlags; + public final InstallArgs installArgs; + + private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting, + UserHandle installForUser, PackageInstalledInfo installResult, int installFlags, + String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) { + this.installArgs = installArgs; + this.pkgSetting = pkgSetting; + this.installForUser = installForUser; + this.installResult = installResult; + this.installFlags = installFlags; + this.volumeUuid = volumeUuid; + this.prepareResult = prepareResult; + this.scanResult = scanResult; + } + } @GuardedBy("mPackages") private static Map<String, ReconciledPackage> reconcilePackagesLocked( - final ReconcileRequest request) throws ReconcileFailure { - return Collections.emptyMap(); + final ReconcileRequest request) + throws ReconcileFailure { + Map<String, ReconciledPackage> result = new ArrayMap<>(request.scannedPackages.size()); + for (String installPackageName : request.installArgs.keySet()) { + final ScanResult scanResult = request.scannedPackages.get(installPackageName); + final InstallArgs installArgs = request.installArgs.get(installPackageName); + final PackageInstalledInfo res = request.installResults.get(installPackageName); + if (scanResult == null || installArgs == null || res == null) { + throw new ReconcileFailure( + "inputs not balanced; missing argument for " + installPackageName); + } + result.put(installPackageName, + new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(), + res, installArgs.installFlags, installArgs.volumeUuid, + request.preparedPackages.get(installPackageName), scanResult)); + } + return result; } @GuardedBy("mPackages") private boolean commitPackagesLocked(final CommitRequest request) { + // TODO: remove any expected failures from this method; this should only be able to fail due + // to unavoidable errors (I/O, etc.) + for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) { + final ScanResult scanResult = reconciledPkg.scanResult; + final ScanRequest scanRequest = scanResult.request; + final PackageParser.Package pkg = scanRequest.pkg; + final String packageName = pkg.packageName; + final PackageInstalledInfo res = reconciledPkg.installResult; + + if (reconciledPkg.prepareResult.replace) { + PackageParser.Package oldPackage = mPackages.get(packageName); + if (reconciledPkg.prepareResult.system) { + // Remove existing system package + removePackageLI(oldPackage, true); + if (!disableSystemPackageLPw(oldPackage, pkg)) { + // We didn't need to disable the .apk as a current system package, + // which means we are replacing another update that is already + // installed. We need to make sure to delete the older one's .apk. + res.removedInfo.args = createInstallArgsForExisting(0, + oldPackage.applicationInfo.getCodePath(), + oldPackage.applicationInfo.getResourcePath(), + getAppDexInstructionSets(oldPackage.applicationInfo)); + } else { + res.removedInfo.args = null; + } + + // Set the update and install times + PackageSetting deletedPkgSetting = (PackageSetting) oldPackage.mExtras; + setInstallAndUpdateTime(pkg, deletedPkgSetting.firstInstallTime, + System.currentTimeMillis()); + + // Update the package dynamic state if succeeded + // Now that the install succeeded make sure we remove data + // directories for any child package the update removed. + final int deletedChildCount = (oldPackage.childPackages != null) + ? oldPackage.childPackages.size() : 0; + final int newChildCount = (pkg.childPackages != null) + ? pkg.childPackages.size() : 0; + for (int i = 0; i < deletedChildCount; i++) { + PackageParser.Package deletedChildPkg = oldPackage.childPackages.get(i); + boolean childPackageDeleted = true; + for (int j = 0; j < newChildCount; j++) { + PackageParser.Package newChildPkg = pkg.childPackages.get(j); + if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) { + childPackageDeleted = false; + break; + } + } + if (childPackageDeleted) { + PackageSetting ps1 = mSettings.getDisabledSystemPkgLPr( + deletedChildPkg.packageName); + if (ps1 != null && res.removedInfo.removedChildPackages != null) { + PackageRemovedInfo removedChildRes = res.removedInfo + .removedChildPackages.get(deletedChildPkg.packageName); + removePackageDataLIF(ps1, request.mAllUsers, removedChildRes, 0, + false); + removedChildRes.removedForAllUsers = mPackages.get(ps1.name) + == null; + } + } + } + } else { + final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0; + final int deleteFlags = PackageManager.DELETE_KEEP_DATA + | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP); + // First delete the existing package while retaining the data directory + if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags, + res.removedInfo, true, pkg)) { + // If the existing package wasn't successfully deleted + res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, + "replaceNonSystemPackageLI"); + return false; + } else { + // Successfully deleted the old package; proceed with replace. + + // If deleted package lived in a container, give users a chance to + // relinquish resources before killing. + if (oldPackage.isForwardLocked() || isExternal(oldPackage)) { + if (DEBUG_INSTALL) { + Slog.i(TAG, "upgrading pkg " + oldPackage + + " is ASEC-hosted -> UNAVAILABLE"); + } + final int[] uidArray = new int[]{oldPackage.applicationInfo.uid}; + final ArrayList<String> pkgList = new ArrayList<>(1); + pkgList.add(oldPackage.applicationInfo.packageName); + sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null); + } + + clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE + | StorageManager.FLAG_STORAGE_CE + | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + } + + + + // Update the in-memory copy of the previous code paths. + PackageSetting ps1 = mSettings.mPackages.get( + reconciledPkg.prepareResult.existingPackage.packageName); + if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP) + == 0) { + if (ps1.mOldCodePaths == null) { + ps1.mOldCodePaths = new ArraySet<>(); + } + Collections.addAll(ps1.mOldCodePaths, oldPackage.baseCodePath); + if (oldPackage.splitCodePaths != null) { + Collections.addAll(ps1.mOldCodePaths, oldPackage.splitCodePaths); + } + } else { + ps1.mOldCodePaths = null; + } + if (ps1.childPackageNames != null) { + for (int i = ps1.childPackageNames.size() - 1; i >= 0; --i) { + final String childPkgName = ps1.childPackageNames.get(i); + final PackageSetting childPs = mSettings.mPackages.get(childPkgName); + childPs.mOldCodePaths = ps1.mOldCodePaths; + } + } + + if (reconciledPkg.installResult.returnCode + == PackageManager.INSTALL_SUCCEEDED) { + PackageSetting ps2 = mSettings.getPackageLPr(pkg.packageName); + if (ps2 != null) { + res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null; + if (res.removedInfo.removedChildPackages != null) { + final int childCount1 = res.removedInfo.removedChildPackages.size(); + // Iterate in reverse as we may modify the collection + for (int i = childCount1 - 1; i >= 0; i--) { + String childPackageName = + res.removedInfo.removedChildPackages.keyAt(i); + if (res.addedChildPackages.containsKey(childPackageName)) { + res.removedInfo.removedChildPackages.removeAt(i); + } else { + PackageRemovedInfo childInfo = res.removedInfo + .removedChildPackages.valueAt(i); + childInfo.removedForAllUsers = mPackages.get( + childInfo.removedPackage) == null; + } + } + } + } + } + } + } + + + + try { + commitScanResultLocked(scanResult); + } catch (PackageManagerException e) { + res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR); + res.setError("Package couldn't be installed in " + pkg.codePath, e); + return false; + } + updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers, + res, reconciledPkg.installForUser, reconciledPkg.installArgs.installReason); + + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null) { + res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); + ps.setUpdateAvailable(false /*updateAvailable*/); + } + final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; + for (int i = 0; i < childCount; i++) { + PackageParser.Package childPkg = pkg.childPackages.get(i); + PackageInstalledInfo childRes = res.addedChildPackages.get( + childPkg.packageName); + PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName); + if (childPs != null) { + childRes.newUsers = childPs.queryInstalledUsers( + sUserManager.getUserIds(), true); + } + } + if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { + updateSequenceNumberLP(ps, res.newUsers); + updateInstantAppInstallerLocked(packageName); + } + } return true; } - @GuardedBy({"mInstallLock", "mPackages", "PackageManagerService.mPackages"}) + /** + * Installs one or more packages atomically. This operation is broken up into four phases: + * <ul> + * <li><b>Prepare</b> + * <br/>Analyzes any current install state, parses the package and does initial + * validation on it.</li> + * <li><b>Scan</b> + * <br/>Interrogates the parsed packages given the context collected in prepare.</li> + * <li><b>Reconcile</b> + * <br/>Validates scanned packages in the context of each other and the current system + * state to ensure that the install will be successful. + * <li><b>Commit</b> + * <br/>Commits all scanned packages and updates system state. This is the only place + * that system state may be modified in the install flow and all predictable errors + * must be determined before this phase.</li> + * </ul> + * + * Failure at any phase will result in a full failure to install all packages. + */ + @GuardedBy({"mInstallLock", "mPackages"}) private void installPackagesLI(List<InstallRequest> requests) { - Map<String, ScanResult> scans = new ArrayMap<>(requests.size()); - for (InstallRequest request : requests) { - // TODO(b/109941548): remove this once we've pulled everything from it and into scan, - // reconcile or commit. - preparePackageLI(request.args, request.res); + final Map<String, ScanResult> scans = new ArrayMap<>(requests.size()); + final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size()); + final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size()); + final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size()); + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI"); + for (InstallRequest request : requests) { + // TODO(b/109941548): remove this once we've pulled everything from it and into + // scan, reconcile or commit. + final PrepareResult prepareResult; + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage"); + prepareResult = preparePackageLI(request.args, request.installResult); + } catch (PrepareFailure prepareFailure) { + request.installResult.setError(prepareFailure.error, + prepareFailure.getMessage()); + request.installResult.origPackage = prepareFailure.conflictingPackage; + request.installResult.origPermission = prepareFailure.conflictingPermission; + return; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + request.installResult.installerPackageName = request.args.installerPackageName; + + final String packageName = prepareResult.packageToScan.packageName; + prepareResults.put(packageName, prepareResult); + installResults.put(packageName, request.installResult); + installArgs.put(packageName, request.args); + try { + final List<ScanResult> scanResults = scanPackageTracedLI( + prepareResult.packageToScan, prepareResult.parseFlags, + prepareResult.scanFlags, System.currentTimeMillis(), + request.args.user); + for (ScanResult result : scanResults) { + if (null != scans.put(result.pkgSetting.pkg.packageName, result)) { + request.installResult.setError( + PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE, + "Duplicate package " + result.pkgSetting.pkg.packageName + + " in atomic install request."); + return; + } + } + } catch (PackageManagerException e) { + request.installResult.setError("Scanning Failed.", e); + return; + } + } + ReconcileRequest reconcileRequest = new ReconcileRequest(scans, installArgs, + installResults, + prepareResults); + CommitRequest commitRequest = null; + synchronized (mPackages) { + Map<String, ReconciledPackage> reconciledPackages; + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); + reconciledPackages = reconcilePackagesLocked(reconcileRequest); + } catch (ReconcileFailure e) { + for (InstallRequest request : requests) { + // TODO(b/109941548): add more concrete failure reasons + request.installResult.setError("Reconciliation failed...", e); + // TODO: return any used system resources + } + return; + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages"); + commitRequest = new CommitRequest(reconciledPackages, + sUserManager.getUserIds()); + if (!commitPackagesLocked(commitRequest)) { + cleanUpCommitFailuresLocked(commitRequest); + return; + } + } finally { + for (PrepareResult result : prepareResults.values()) { + if (result.freezer != null) { + result.freezer.close(); + } + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + executePostCommitSteps(commitRequest); + } finally { + for (PrepareResult result : prepareResults.values()) { + if (result.freezer != null) { + result.freezer.close(); + } + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + /** + * On successful install, executes remaining steps after commit completes and the package lock + * is released. + */ + private void executePostCommitSteps(CommitRequest commitRequest) { + for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) { + final boolean forwardLocked = + ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0); + final boolean instantApp = + ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0); + final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg; + final String packageName = pkg.packageName; + prepareAppDataAfterInstallLIF(pkg); + if (reconciledPkg.prepareResult.clearCodeCache) { + clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE + | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + } + if (reconciledPkg.prepareResult.replace) { + mDexManager.notifyPackageUpdated(pkg.packageName, + pkg.baseCodePath, pkg.splitCodePaths); + } + + // Prepare the application profiles for the new code paths. + // This needs to be done before invoking dexopt so that any install-time profile + // can be used for optimizations. + mArtManagerService.prepareAppProfiles( + pkg, resolveUserIds(reconciledPkg.installForUser.getIdentifier())); + + // Check whether we need to dexopt the app. + // + // NOTE: it is IMPORTANT to call dexopt: + // - after doRename which will sync the package data from PackageParser.Package and + // its corresponding ApplicationInfo. + // - after installNewPackageLIF or replacePackageLIF which will update result with the + // uid of the application (pkg.applicationInfo.uid). + // This update happens in place! + // + // We only need to dexopt if the package meets ALL of the following conditions: + // 1) it is not forward locked. + // 2) it is not on on an external ASEC container. + // 3) it is not an instant app or if it is then dexopt is enabled via gservices. + // 4) it is not debuggable. + // + // Note that we do not dexopt instant apps by default. dexopt can take some time to + // complete, so we skip this step during installation. Instead, we'll take extra time + // the first time the instant app starts. It's preferred to do it this way to provide + // continuous progress to the useur instead of mysteriously blocking somewhere in the + // middle of running an instant app. The default behaviour can be overridden + // via gservices. + final boolean performDexopt = !forwardLocked + && !pkg.applicationInfo.isExternalAsec() + && (!instantApp || Global.getInt(mContext.getContentResolver(), + Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) + && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0); + + if (performDexopt) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); + // Do not run PackageDexOptimizer through the local performDexOpt + // method because `pkg` may not be in `mPackages` yet. + // + // Also, don't fail application installs if the dexopt step fails. + DexoptOptions dexoptOptions = new DexoptOptions(packageName, + REASON_INSTALL, + DexoptOptions.DEXOPT_BOOT_COMPLETE + | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); + mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles, + null /* instructionSets */, + getOrCreateCompilerPackageStats(pkg), + mDexManager.getPackageUseInfoOrDefault(packageName), + dexoptOptions); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } - // TODO(b/109941548): scan package and get result + // Notify BackgroundDexOptService that the package has been changed. + // If this is an update of a package which used to fail to compile, + // BackgroundDexOptService will remove it from its blacklist. + // TODO: Layering violation + BackgroundDexOptService.notifyPackageChanged(packageName); } - ReconcileRequest reconcileRequest = new ReconcileRequest(scans); - Map<String, ReconciledPackage> reconciledPackages; - try { - reconciledPackages = reconcilePackagesLocked(reconcileRequest); - } catch (ReconcileFailure e) { - // TODO(b/109941548): set install args error - return; + + } + + private void cleanUpCommitFailuresLocked(CommitRequest request) { + final Map<String, ReconciledPackage> reconciledPackages = request.reconciledPackages; + final int[] allUsers = request.mAllUsers; + for (ReconciledPackage reconciledPackage : reconciledPackages.values()) { + final String pkgName1 = reconciledPackage.pkgSetting.pkg.packageName; + if (reconciledPackage.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) { + reconciledPackage.installResult.setError( + PackageManager.INSTALL_FAILED_INTERNAL_ERROR, "Commit failed..."); + } + final PackageParser.Package oldPackage = + reconciledPackage.prepareResult.existingPackage; + final PackageParser.Package newPackage = reconciledPackage.pkgSetting.pkg; + if (reconciledPackage.prepareResult.system) { + // Re installation failed. Restore old information + // Remove new pkg information + if (newPackage != null) { + removeInstalledPackageLI(newPackage, true); + } + // Add back the old system package + PackageParser.Package restoredPkg = null; + try { + final List<ScanResult> restoreResults = scanPackageTracedLI(oldPackage, + reconciledPackage.prepareResult.parseFlags, SCAN_UPDATE_SIGNATURE, 0, + reconciledPackage.installForUser); + commitSuccessfulScanResults(restoreResults); + restoredPkg = restoreResults.get(0).pkgSetting.pkg; + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to restore original package: " + e.getMessage()); + } + synchronized (mPackages) { + final boolean disabledSystem; + // Remove existing system package + removePackageLI(reconciledPackage.scanResult.pkgSetting.pkg, true); + disabledSystem = disableSystemPackageLPw( + reconciledPackage.scanResult.pkgSetting.pkg, restoredPkg); + if (disabledSystem) { + enableSystemPackageLPw(restoredPkg); + } + // Ensure the installer package name up to date + setInstallerPackageNameLPw(reconciledPackage.scanResult.pkgSetting.pkg, + reconciledPackage.installArgs.installerPackageName); + // Update permissions for restored package + mPermissionManager.updatePermissions( + restoredPkg.packageName, restoredPkg, false, mPackages.values(), + mPermissionCallback); + mSettings.writeLPr(); + } + Slog.i(TAG, "Successfully restored package : " + restoredPkg.packageName + + " after failed upgrade"); + } else if (reconciledPackage.prepareResult.replace) { + if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName1); + + // Revert all internal state mutations and added folders for the failed install + boolean deletedPkg = deletePackageLIF(pkgName1, null, true, + allUsers, /*TODO: deleteFlags*/ 0, + reconciledPackage.installResult.removedInfo, true, null); + + // Restore the old package + if (deletedPkg) { + if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + oldPackage); + File restoreFile = new File(oldPackage.codePath); + // Parse old package + boolean oldExternal = isExternal(oldPackage); + int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY + | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0) + | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0); + int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME; + try { + scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, + /* origUpdateTime */ System.currentTimeMillis(), null); + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to restore package : " + pkgName1 + + " after failed upgrade: " + + e.getMessage()); + return; + } + + synchronized (mPackages) { + // Ensure the installer package name up to date + setInstallerPackageNameLPw(oldPackage, + reconciledPackage.installArgs.installerPackageName); + + // Update permissions for restored package + mPermissionManager.updatePermissions( + oldPackage.packageName, oldPackage, false, mPackages.values(), + mPermissionCallback); + + mSettings.writeLPr(); + } + + Slog.i(TAG, "Successfully restored package : " + pkgName1 + + " after failed upgrade"); + } + } else { + // Remove package from internal structures, but keep around any + // data that might have already existed + deletePackageLIF(pkgName1, UserHandle.ALL, false, null, + PackageManager.DELETE_KEEP_DATA, + reconciledPackage.installResult.removedInfo, true, null); + } } - CommitRequest request = new CommitRequest(reconciledPackages); - if (!commitPackagesLocked(request)) { - // TODO(b/109941548): set install args error - return; + } + + /** + * The set of data needed to successfully install the prepared package. This includes data that + * will be used to scan and reconcile the package. + */ + private static class PrepareResult { + public final int installReason; + public final String volumeUuid; + public final String installerPackageName; + public final UserHandle user; + public final boolean replace; + public final int scanFlags; + public final int parseFlags; + @Nullable /* The original Package if it is being replaced, otherwise {@code null} */ + public final PackageParser.Package existingPackage; + public final PackageParser.Package packageToScan; + public final boolean clearCodeCache; + public final boolean system; + /* The original package name if it was changed during an update, otherwise {@code null}. */ + @Nullable + public final String renamedPackage; + public final PackageFreezer freezer; + + private PrepareResult(int installReason, String volumeUuid, + String installerPackageName, UserHandle user, boolean replace, int scanFlags, + int parseFlags, PackageParser.Package existingPackage, + PackageParser.Package packageToScan, boolean clearCodeCache, boolean system, + String renamedPackage, + PackageFreezer freezer) { + this.installReason = installReason; + this.volumeUuid = volumeUuid; + this.installerPackageName = installerPackageName; + this.user = user; + this.replace = replace; + this.scanFlags = scanFlags; + this.parseFlags = parseFlags; + this.existingPackage = existingPackage; + this.packageToScan = packageToScan; + this.clearCodeCache = clearCodeCache; + this.system = system; + this.renamedPackage = renamedPackage; + this.freezer = freezer; + } + } + + private static class PrepareFailure extends PackageManagerException { + + public String conflictingPackage; + public String conflictingPermission; + + PrepareFailure(int error) { + super(error, "Failed to prepare for install."); + } + + PrepareFailure(int error, String detailMessage) { + super(error, detailMessage); + } + + PrepareFailure(String message, Exception e) { + super(e instanceof PackageParserException + ? ((PackageParserException) e).error + : ((PackageManagerException) e).error, + ExceptionUtils.getCompleteMessage(message, e)); + } + + PrepareFailure conflictsWithExistingPermission(String conflictingPermission, + String conflictingPackage) { + this.conflictingPermission = conflictingPermission; + this.conflictingPackage = conflictingPackage; + return this; } - // TODO(b/109941548) post-commit actions (dex-opt, etc.) } - @Deprecated @GuardedBy("mInstallLock") - private void preparePackageLI(InstallArgs args, PackageInstalledInfo res) { + private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res) + throws PrepareFailure { final int installFlags = args.installFlags; final String installerPackageName = args.installerPackageName; final String volumeUuid = args.volumeUuid; @@ -15494,7 +15582,6 @@ public class PackageManagerService extends IPackageManager.Stub final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0); final boolean virtualPreload = ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); - boolean replace = false; @ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE; if (args.move != null) { // moving a complete application; perform an initial scan on the new install location @@ -15513,18 +15600,13 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags |= SCAN_AS_VIRTUAL_PRELOAD; } - // Result object to be returned - res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); - res.installerPackageName = installerPackageName; - if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile); // Sanity check if (instantApp && (forwardLocked || onExternal)) { Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked + " external=" + onExternal); - res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID); - return; + throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID); } // Retrieve PackageSettings and parse package @@ -15533,6 +15615,7 @@ public class PackageManagerService extends IPackageManager.Stub | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0) | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0); + PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setDisplayMetrics(mMetrics); @@ -15544,8 +15627,7 @@ public class PackageManagerService extends IPackageManager.Stub pkg = pp.parsePackage(tmpPackageFile, parseFlags); DexMetadataHelper.validatePackageDexMetadata(pkg); } catch (PackageParserException e) { - res.setError("Failed parse during installPackageLI", e); - return; + throw new PrepareFailure("Failed parse during installPackageLI", e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -15555,16 +15637,14 @@ public class PackageManagerService extends IPackageManager.Stub if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) { Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target at least O"); - res.setError(INSTALL_FAILED_INSTANT_APP_INVALID, + throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID, "Instant app package must target at least O"); - return; } if (pkg.mSharedUserId != null) { Slog.w(TAG, "Instant app package " + pkg.packageName + " may not declare sharedUserId."); - res.setError(INSTALL_FAILED_INSTANT_APP_INVALID, + throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID, "Instant app package may not declare a sharedUserId"); - return; } } @@ -15575,9 +15655,8 @@ public class PackageManagerService extends IPackageManager.Stub // No static shared libs on external storage if (onExternal) { Slog.i(TAG, "Static shared libs can only be installed on internal storage."); - res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, + throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION, "Packages declaring static-shared libs cannot be updated"); - return; } } @@ -15616,10 +15695,9 @@ public class PackageManagerService extends IPackageManager.Stub } String pkgName = res.name = pkg.packageName; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) { + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0) { if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) { - res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI"); - return; + throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI"); } } @@ -15631,22 +15709,21 @@ public class PackageManagerService extends IPackageManager.Stub PackageParser.collectCertificates(pkg, false /* skipVerify */); } } catch (PackageParserException e) { - res.setError("Failed collect during installPackageLI", e); - return; + throw new PrepareFailure("Failed collect during installPackageLI", e); } if (instantApp && pkg.mSigningDetails.signatureSchemeVersion < SignatureSchemeVersion.SIGNING_BLOCK_V2) { Slog.w(TAG, "Instant app package " + pkg.packageName + " is not signed with at least APK Signature Scheme v2"); - res.setError(INSTALL_FAILED_INSTANT_APP_INVALID, + throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID, "Instant app package must be signed with APK Signature Scheme v2 or greater"); - return; } // Get rid of all references to package scan path via parser. pp = null; boolean systemApp = false; + boolean replace = false; synchronized (mPackages) { // Check if installing already existing package if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { @@ -15661,8 +15738,10 @@ public class PackageManagerService extends IPackageManager.Stub pkg.setPackageName(oldName); pkgName = pkg.packageName; replace = true; - if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName=" - + oldName + " pkgName=" + pkgName); + if (DEBUG_INSTALL) { + Slog.d(TAG, "Replacing existing renamed package: oldName=" + + oldName + " pkgName=" + pkgName); + } } else if (mPackages.containsKey(pkgName)) { // This package, under its official name, already exists // on the device; we should replace it. @@ -15672,11 +15751,11 @@ public class PackageManagerService extends IPackageManager.Stub // Child packages are installed through the parent package if (pkg.parentPackage != null) { - res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + throw new PrepareFailure( + PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, "Package " + pkg.packageName + " is child of package " + pkg.parentPackage.parentPackage + ". Child packages " + "can be updated only through the parent package."); - return; } if (replace) { @@ -15686,26 +15765,25 @@ public class PackageManagerService extends IPackageManager.Stub final int newTargetSdk = pkg.applicationInfo.targetSdkVersion; if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1 && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) { - res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE, + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE, "Package " + pkg.packageName + " new target SDK " + newTargetSdk + " doesn't support runtime permissions but the old" + " target SDK " + oldTargetSdk + " does."); - return; } // Prevent persistent apps from being updated if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) { - res.setError(PackageManager.INSTALL_FAILED_INVALID_APK, + throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK, "Package " + oldPackage.packageName + " is a persistent app. " + "Persistent apps are not updateable."); - return; } // Prevent installing of child packages if (oldPackage.parentPackage != null) { - res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + throw new PrepareFailure( + PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, "Package " + pkg.packageName + " is child of package " + oldPackage.parentPackage + ". Child packages " + "can be updated only through the parent package."); - return; } } } @@ -15732,10 +15810,9 @@ public class PackageManagerService extends IPackageManager.Stub final KeySetManagerService ksms = mSettings.mKeySetManagerService; if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) { if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) { - res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + pkg.packageName + " upgrade keys do not match the " + "previously installed version"); - return; } } else { try { @@ -15752,8 +15829,7 @@ public class PackageManagerService extends IPackageManager.Stub } } } catch (PackageManagerException e) { - res.setError(e.error, e.getMessage()); - return; + throw new PrepareFailure(e.error, e.getMessage()); } } @@ -15764,8 +15840,9 @@ public class PackageManagerService extends IPackageManager.Stub res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } + int N = pkg.permissions.size(); - for (int i = N-1; i >= 0; i--) { + for (int i = N - 1; i >= 0; i--) { final PackageParser.Permission perm = pkg.permissions.get(i); final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name); @@ -15790,7 +15867,7 @@ public class PackageManagerService extends IPackageManager.Stub final KeySetManagerService ksms = mSettings.mKeySetManagerService; if (sourcePackageName.equals(pkg.packageName) && (ksms.shouldCheckUpgradeKeySetLocked( - sourcePackageSetting, scanFlags))) { + sourcePackageSetting, scanFlags))) { sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg); } else { @@ -15798,12 +15875,12 @@ public class PackageManagerService extends IPackageManager.Stub // package's certificate has rotated from the current one, or if it is an // older certificate with which the current is ok with sharing permissions if (sourcePackageSetting.signatures.mSigningDetails.checkCapability( - pkg.mSigningDetails, - PackageParser.SigningDetails.CertCapabilities.PERMISSION)) { + pkg.mSigningDetails, + PackageParser.SigningDetails.CertCapabilities.PERMISSION)) { sigsOk = true; } else if (pkg.mSigningDetails.checkCapability( - sourcePackageSetting.signatures.mSigningDetails, - PackageParser.SigningDetails.CertCapabilities.PERMISSION)) { + sourcePackageSetting.signatures.mSigningDetails, + PackageParser.SigningDetails.CertCapabilities.PERMISSION)) { // the scanned package checks out, has signing certificate rotation // history, and is newer; bring it over @@ -15818,12 +15895,13 @@ public class PackageManagerService extends IPackageManager.Stub // install to proceed; we fail the install on all other permission // redefinitions. if (!sourcePackageName.equals("android")) { - res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package " - + pkg.packageName + " attempting to redeclare permission " - + perm.info.name + " already owned by " + sourcePackageName); - res.origPermission = perm.info.name; - res.origPackage = sourcePackageName; - return; + throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package " + + pkg.packageName + + " attempting to redeclare permission " + + perm.info.name + " already owned by " + + sourcePackageName) + .conflictsWithExistingPermission(perm.info.name, + sourcePackageName); } else { Slog.w(TAG, "Package " + pkg.packageName + " attempting to redeclare system permission " @@ -15852,14 +15930,12 @@ public class PackageManagerService extends IPackageManager.Stub if (systemApp) { if (onExternal) { // Abort update; system app can't be replaced with app on sdcard - res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, + throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION, "Cannot install updates to system apps on sdcard"); - return; } else if (instantApp) { // Abort update; system app can't be replaced with an instant app - res.setError(INSTALL_FAILED_INSTANT_APP_INVALID, + throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID, "Cannot update a system app with an instant app"); - return; } } @@ -15887,13 +15963,13 @@ public class PackageManagerService extends IPackageManager.Stub try { String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ? - args.abiOverride : pkg.cpuAbiOverride); + args.abiOverride : pkg.cpuAbiOverride); final boolean extractNativeLibs = !pkg.isLibrary(); derivePackageAbi(pkg, abiOverride, extractNativeLibs); } catch (PackageManagerException pme) { Slog.e(TAG, "Error deriving application ABI", pme); - res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI"); - return; + throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, + "Error deriving application ABI"); } // Shared libraries for the package need to be updated. @@ -15907,8 +15983,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!args.doRename(res.returnCode, pkg)) { - res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); - return; + throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); } if (PackageManagerServiceUtils.isApkVerityEnabled()) { @@ -15922,7 +15997,6 @@ public class PackageManagerService extends IPackageManager.Stub apkPath = pkg.baseCodePath; } } - if (apkPath != null) { final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(apkPath); @@ -15934,16 +16008,15 @@ public class PackageManagerService extends IPackageManager.Stub mInstaller.installApkVerity(apkPath, fd, result.getContentSize()); mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash); } catch (InstallerException | IOException | DigestException | - NoSuchAlgorithmException e) { - res.setError(INSTALL_FAILED_INTERNAL_ERROR, + NoSuchAlgorithmException e) { + throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, "Failed to set up verity: " + e); - return; } finally { IoUtils.closeQuietly(fd); } } else if (result.isFailed()) { - res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to generate verity"); - return; + throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, + "Failed to generate verity"); } else { // Do nothing if verity is skipped. Will fall back to full apk verification on // reboot. @@ -15958,109 +16031,293 @@ public class PackageManagerService extends IPackageManager.Stub Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName); } } + final PackageFreezer freezer = + freezePackageForInstall(pkgName, installFlags, "installPackageLI"); + boolean shouldCloseFreezerBeforeReturn = true; + try { + final PackageParser.Package existingPackage; + String renamedPackage = null; + boolean clearCodeCache = false; + boolean sysPkg = false; + String targetVolumeUuid = volumeUuid; + int targetScanFlags = scanFlags; + int targetParseFlags = parseFlags; - try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, - "installPackageLI")) { if (replace) { + targetVolumeUuid = null; if (pkg.applicationInfo.isStaticSharedLibrary()) { // Static libs have a synthetic package name containing the version // and cannot be updated as an update would get a new package name, // unless this is the exact same version code which is useful for // development. PackageParser.Package existingPkg = mPackages.get(pkg.packageName); - if (existingPkg != null && - existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) { - res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring " - + "static-shared libs cannot be updated"); - return; + if (existingPkg != null + && existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) { + throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE, + "Packages declaring " + + "static-shared libs cannot be updated"); } } - replacePackageLIF(pkg, parseFlags, scanFlags, args.user, - installerPackageName, res, args.installReason); - } else { - installNewPackageLIF(pkg, parseFlags, scanFlags, - args.user, installerPackageName, volumeUuid, res, args.installReason); - } - } - // Prepare the application profiles for the new code paths. - // This needs to be done before invoking dexopt so that any install-time profile - // can be used for optimizations. - mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier())); + final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; - // Check whether we need to dexopt the app. - // - // NOTE: it is IMPORTANT to call dexopt: - // - after doRename which will sync the package data from PackageParser.Package and its - // corresponding ApplicationInfo. - // - after installNewPackageLIF or replacePackageLIF which will update result with the - // uid of the application (pkg.applicationInfo.uid). - // This update happens in place! - // - // We only need to dexopt if the package meets ALL of the following conditions: - // 1) it is not forward locked. - // 2) it is not on on an external ASEC container. - // 3) it is not an instant app or if it is then dexopt is enabled via gservices. - // 4) it is not debuggable. - // - // Note that we do not dexopt instant apps by default. dexopt can take some time to - // complete, so we skip this step during installation. Instead, we'll take extra time - // the first time the instant app starts. It's preferred to do it this way to provide - // continuous progress to the useur instead of mysteriously blocking somewhere in the - // middle of running an instant app. The default behaviour can be overridden - // via gservices. - final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED) - && !forwardLocked - && !pkg.applicationInfo.isExternalAsec() - && (!instantApp || Global.getInt(mContext.getContentResolver(), - Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) - && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0); - - if (performDexopt) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); - // Do not run PackageDexOptimizer through the local performDexOpt - // method because `pkg` may not be in `mPackages` yet. - // - // Also, don't fail application installs if the dexopt step fails. - DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName, - REASON_INSTALL, - DexoptOptions.DEXOPT_BOOT_COMPLETE | - DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); - mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles, - null /* instructionSets */, - getOrCreateCompilerPackageStats(pkg), - mDexManager.getPackageUseInfoOrDefault(pkg.packageName), - dexoptOptions); - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } + final PackageParser.Package oldPackage; + final PackageSetting ps; + final String pkgName11 = pkg.packageName; + final int[] allUsers; + final int[] installedUsers; - // Notify BackgroundDexOptService that the package has been changed. - // If this is an update of a package which used to fail to compile, - // BackgroundDexOptService will remove it from its blacklist. - // TODO: Layering violation - BackgroundDexOptService.notifyPackageChanged(pkg.packageName); + synchronized (mPackages) { + oldPackage = mPackages.get(pkgName11); + existingPackage = oldPackage; + if (DEBUG_INSTALL) { + Slog.d(TAG, + "replacePackageLI: new=" + pkg + ", old=" + oldPackage); + } - synchronized (mPackages) { - final PackageSetting ps = mSettings.mPackages.get(pkgName); - if (ps != null) { - res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); - ps.setUpdateAvailable(false /*updateAvailable*/); - } + // don't allow upgrade to target a release SDK from a pre-release SDK + final boolean oldTargetsPreRelease = oldPackage.applicationInfo.targetSdkVersion + == Build.VERSION_CODES.CUR_DEVELOPMENT; + final boolean newTargetsPreRelease = pkg.applicationInfo.targetSdkVersion + == Build.VERSION_CODES.CUR_DEVELOPMENT; + if (oldTargetsPreRelease + && !newTargetsPreRelease + && ((parseFlags & PackageParser.PARSE_FORCE_SDK) == 0)) { + Slog.w(TAG, "Can't install package targeting released sdk"); + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); + } - final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; - for (int i = 0; i < childCount; i++) { - PackageParser.Package childPkg = pkg.childPackages.get(i); - PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName); - PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName); - if (childPs != null) { - childRes.newUsers = childPs.queryInstalledUsers( - sUserManager.getUserIds(), true); + ps = mSettings.mPackages.get(pkgName11); + + // verify signatures are valid + final KeySetManagerService ksms = mSettings.mKeySetManagerService; + if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) { + if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "New package not signed by keys specified by upgrade-keysets: " + + pkgName11); + } + } else { + // default to original signature matching + if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails, + SigningDetails.CertCapabilities.INSTALLED_DATA) + && !oldPackage.mSigningDetails.checkCapability( + pkg.mSigningDetails, + SigningDetails.CertCapabilities.ROLLBACK)) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "New package has a different signature: " + pkgName11); + } + } + + // don't allow a system upgrade unless the upgrade hash matches + if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) { + final byte[] digestBytes; + try { + final MessageDigest digest = MessageDigest.getInstance("SHA-512"); + updateDigest(digest, new File(pkg.baseCodePath)); + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + for (String path : pkg.splitCodePaths) { + updateDigest(digest, new File(path)); + } + } + digestBytes = digest.digest(); + } catch (NoSuchAlgorithmException | IOException e) { + throw new PrepareFailure(INSTALL_FAILED_INVALID_APK, + "Could not compute hash: " + pkgName11); + } + if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) { + throw new PrepareFailure(INSTALL_FAILED_INVALID_APK, + "New package fails restrict-update check: " + pkgName11); + } + // retain upgrade restriction + pkg.restrictUpdateHash = oldPackage.restrictUpdateHash; + } + + // Check for shared user id changes + String invalidPackageName = + getParentOrChildPackageChangedSharedUser(oldPackage, pkg); + if (invalidPackageName != null) { + throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE, + "Package " + invalidPackageName + " tried to change user " + + oldPackage.mSharedUserId); + } + + // check if the new package supports all of the abis which the old package + // supports + boolean oldPkgSupportMultiArch = + oldPackage.applicationInfo.secondaryCpuAbi != null; + boolean newPkgSupportMultiArch = pkg.applicationInfo.secondaryCpuAbi != null; + if (isSystemApp(oldPackage) && oldPkgSupportMultiArch + && !newPkgSupportMultiArch) { + throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, + "Update to package " + pkgName11 + " doesn't support multi arch"); + } + + // In case of rollback, remember per-user/profile install state + allUsers = sUserManager.getUserIds(); + installedUsers = ps.queryInstalledUsers(allUsers, true); + + + // don't allow an upgrade from full to ephemeral + if (isInstantApp) { + if (args.user == null || args.user.getIdentifier() == UserHandle.USER_ALL) { + for (int currentUser : allUsers) { + if (!ps.getInstantApp(currentUser)) { + // can't downgrade from full to instant + Slog.w(TAG, + "Can't replace full app with instant app: " + pkgName11 + + " for user: " + currentUser); + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID); + } + } + } else if (!ps.getInstantApp(args.user.getIdentifier())) { + // can't downgrade from full to instant + Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11 + + " for user: " + args.user.getIdentifier()); + throw new PrepareFailure( + PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID); + } + } } - } - if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - updateSequenceNumberLP(ps, res.newUsers); - updateInstantAppInstallerLocked(pkgName); + // Update what is removed + res.removedInfo = new PackageRemovedInfo(this); + res.removedInfo.uid = oldPackage.applicationInfo.uid; + res.removedInfo.removedPackage = oldPackage.packageName; + res.removedInfo.installerPackageName = ps.installerPackageName; + res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null; + res.removedInfo.isUpdate = true; + res.removedInfo.origUsers = installedUsers; + res.removedInfo.installReasons = new SparseArray<>(installedUsers.length); + for (int i = 0; i < installedUsers.length; i++) { + final int userId = installedUsers[i]; + res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId)); + } + + final int childCount = (oldPackage.childPackages != null) + ? oldPackage.childPackages.size() : 0; + for (int i = 0; i < childCount; i++) { + boolean childPackageUpdated = false; + PackageParser.Package childPkg = oldPackage.childPackages.get(i); + final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName); + if (res.addedChildPackages != null) { + PackageInstalledInfo childRes = res.addedChildPackages.get( + childPkg.packageName); + if (childRes != null) { + childRes.removedInfo.uid = childPkg.applicationInfo.uid; + childRes.removedInfo.removedPackage = childPkg.packageName; + if (childPs != null) { + childRes.removedInfo.installerPackageName = + childPs.installerPackageName; + } + childRes.removedInfo.isUpdate = true; + childRes.removedInfo.installReasons = res.removedInfo.installReasons; + childPackageUpdated = true; + } + } + if (!childPackageUpdated) { + PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this); + childRemovedRes.removedPackage = childPkg.packageName; + if (childPs != null) { + childRemovedRes.installerPackageName = childPs.installerPackageName; + } + childRemovedRes.isUpdate = false; + childRemovedRes.dataRemoved = true; + synchronized (mPackages) { + if (childPs != null) { + childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, + true); + } + } + if (res.removedInfo.removedChildPackages == null) { + res.removedInfo.removedChildPackages = new ArrayMap<>(); + } + res.removedInfo.removedChildPackages.put(childPkg.packageName, + childRemovedRes); + } + } + + sysPkg = (isSystemApp(oldPackage)); + if (sysPkg) { + // Set the system/privileged/oem/vendor/product flags as needed + final boolean privileged = isPrivilegedApp(oldPackage); + final boolean oem = isOemApp(oldPackage); + final boolean vendor = isVendorApp(oldPackage); + final boolean product = isProductApp(oldPackage); + final @ParseFlags int systemParseFlags = parseFlags; + final @ScanFlags int systemScanFlags = scanFlags + | SCAN_AS_SYSTEM + | (privileged ? SCAN_AS_PRIVILEGED : 0) + | (oem ? SCAN_AS_OEM : 0) + | (vendor ? SCAN_AS_VENDOR : 0) + | (product ? SCAN_AS_PRODUCT : 0); + + if (DEBUG_INSTALL) { + Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg + + ", old=" + oldPackage); + } + clearCodeCache = true; + res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); + pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, + ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); + targetParseFlags = systemParseFlags; + targetScanFlags = systemScanFlags; + } else { // non system replace + replace = true; + if (DEBUG_INSTALL) { + Slog.d(TAG, + "replaceNonSystemPackageLI: new=" + pkg + ", old=" + + oldPackage); + } + + String pkgName1 = oldPackage.packageName; + boolean deletedPkg = true; + boolean addedPkg = false; + boolean updatedSettings = false; + + final long origUpdateTime = (pkg.mExtras != null) + ? ((PackageSetting) pkg.mExtras).lastUpdateTime : 0; + + } + } else { // new package install + replace = false; + existingPackage = null; + // Remember this for later, in case we need to rollback this install + String pkgName1 = pkg.packageName; + + if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); + + // TODO(patb): MOVE TO RECONCILE + synchronized (mPackages) { + renamedPackage = mSettings.getRenamedPackageLPr(pkgName1); + if (renamedPackage != null) { + // A package with the same name is already installed, though + // it has been renamed to an older name. The package we + // are trying to install should be installed as an update to + // the existing one, but that has not been requested, so bail. + throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS, + "Attempt to re-install " + pkgName1 + + " without first uninstalling package running as " + + renamedPackage); + } + if (mPackages.containsKey(pkgName1)) { + // Don't allow installation over an existing package with the same name. + throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS, + "Attempt to re-install " + pkgName1 + + " without first uninstalling."); + } + } + } + // we're passing the freezer back to be closed in a later phase of install + shouldCloseFreezerBeforeReturn = false; + return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName, + args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg, + clearCodeCache, sysPkg, renamedPackage, freezer); + } finally { + if (shouldCloseFreezerBeforeReturn) { + freezer.close(); } } } @@ -16870,7 +17127,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - removePackageLI(ps, (flags & PackageManager.DELETE_CHATTY) != 0); + removePackageLI(ps.name, (flags & PackageManager.DELETE_CHATTY) != 0); if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { final PackageParser.Package resolvedPkg; diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 727fb15e616a..b8506136d071 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -207,4 +207,13 @@ public final class PackageSetting extends PackageSettingBase { writeUsersInfoToProto(proto, PackageProto.USERS); proto.end(packageToken); } + + /** Updates all fields in the current setting from another. */ + public void updateFrom(PackageSetting other) { + super.updateFrom(other); + appId = other.appId; + pkg = other.pkg; + sharedUserId = other.sharedUserId; + sharedUser = other.sharedUser; + } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 138594ccb4d6..fd6aceb1ce6b 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -26,7 +26,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.Signature; -import android.os.BaseBundle; import android.os.PersistableBundle; import android.service.pm.PackageProto; import android.util.ArraySet; @@ -109,7 +108,7 @@ public abstract class PackageSettingBase extends SettingBase { // Whether this package is currently stopped, thus can not be // started until explicitly launched by the user. - private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>(); + private final SparseArray<PackageUserState> mUserState = new SparseArray<>(); /** * Non-persisted value. During an "upgrade without restart", we need the set @@ -118,7 +117,7 @@ public abstract class PackageSettingBase extends SettingBase { * restart, this field will be cleared since the classloader would be created * using the full set of code paths when the package's process is started. */ - Set<String> oldCodePaths; + Set<String> mOldCodePaths; /** Package name of the app that installed this package */ String installerPackageName; @@ -223,7 +222,7 @@ public abstract class PackageSettingBase extends SettingBase { /** * Makes a shallow copy of the given package settings. * - * NOTE: For some fields [such as keySetData, signatures, userState, verificationInfo, etc...], + * NOTE: For some fields [such as keySetData, signatures, mUserState, verificationInfo, etc...], * the original object is copied and a new one is not created. */ public void copyFrom(PackageSettingBase orig) { @@ -244,7 +243,7 @@ public abstract class PackageSettingBase extends SettingBase { keySetData = orig.keySetData; lastUpdateTime = orig.lastUpdateTime; legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString; - // Intentionally skip oldCodePaths; it's not relevant for copies + // Intentionally skip mOldCodePaths; it's not relevant for copies parentPackageName = orig.parentPackageName; primaryCpuAbiString = orig.primaryCpuAbiString; resourcePath = orig.resourcePath; @@ -253,9 +252,9 @@ public abstract class PackageSettingBase extends SettingBase { signatures = orig.signatures; timeStamp = orig.timeStamp; uidError = orig.uidError; - userState.clear(); - for (int i=0; i<orig.userState.size(); i++) { - userState.put(orig.userState.keyAt(i), orig.userState.valueAt(i)); + mUserState.clear(); + for (int i = 0; i < orig.mUserState.size(); i++) { + mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i)); } verificationInfo = orig.verificationInfo; versionCode = orig.versionCode; @@ -271,16 +270,16 @@ public abstract class PackageSettingBase extends SettingBase { } private PackageUserState modifyUserState(int userId) { - PackageUserState state = userState.get(userId); + PackageUserState state = mUserState.get(userId); if (state == null) { state = new PackageUserState(); - userState.put(userId, state); + mUserState.put(userId, state); } return state; } public PackageUserState readUserState(int userId) { - PackageUserState state = userState.get(userId); + PackageUserState state = mUserState.get(userId); if (state == null) { return DEFAULT_USER_STATE; } @@ -330,7 +329,7 @@ public abstract class PackageSettingBase extends SettingBase { /** Only use for testing. Do NOT use in production code. */ @VisibleForTesting SparseArray<PackageUserState> getUserState() { - return userState; + return mUserState; } boolean isAnyInstalled(int[] users) { @@ -536,14 +535,14 @@ public abstract class PackageSettingBase extends SettingBase { } void removeUser(int userId) { - userState.delete(userId); + mUserState.delete(userId); } public int[] getNotInstalledUserIds() { int count = 0; - int userStateCount = userState.size(); + int userStateCount = mUserState.size(); for (int i = 0; i < userStateCount; i++) { - if (userState.valueAt(i).installed == false) { + if (!mUserState.valueAt(i).installed) { count++; } } @@ -551,8 +550,8 @@ public abstract class PackageSettingBase extends SettingBase { int[] excludedUserIds = new int[count]; int idx = 0; for (int i = 0; i < userStateCount; i++) { - if (userState.valueAt(i).installed == false) { - excludedUserIds[idx++] = userState.keyAt(i); + if (!mUserState.valueAt(i).installed) { + excludedUserIds[idx++] = mUserState.keyAt(i); } } return excludedUserIds; @@ -591,11 +590,11 @@ public abstract class PackageSettingBase extends SettingBase { } protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) { - int count = userState.size(); + int count = mUserState.size(); for (int i = 0; i < count; i++) { final long userToken = proto.start(fieldId); - final int userId = userState.keyAt(i); - final PackageUserState state = userState.valueAt(i); + final int userId = mUserState.keyAt(i); + final PackageUserState state = mUserState.valueAt(i); proto.write(PackageProto.UserInfoProto.ID, userId); final int installType; if (state.instantApp) { @@ -630,4 +629,48 @@ public abstract class PackageSettingBase extends SettingBase { PackageUserState userState = readUserState(userId); return userState.harmfulAppWarning; } + + protected PackageSettingBase updateFrom(PackageSettingBase other) { + super.copyFrom(other); + this.parentPackageName = other.parentPackageName; + this.childPackageNames = other.childPackageNames; + this.codePath = other.codePath; + this.codePathString = other.codePathString; + this.resourcePath = other.resourcePath; + this.resourcePathString = other.resourcePathString; + this.usesStaticLibraries = other.usesStaticLibraries; + this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions; + this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString; + this.primaryCpuAbiString = other.primaryCpuAbiString; + this.secondaryCpuAbiString = other.secondaryCpuAbiString; + this.cpuAbiOverrideString = other.cpuAbiOverrideString; + this.timeStamp = other.timeStamp; + this.firstInstallTime = other.firstInstallTime; + this.lastUpdateTime = other.lastUpdateTime; + this.versionCode = other.versionCode; + this.uidError = other.uidError; + this.signatures = other.signatures; + this.installPermissionsFixed = other.installPermissionsFixed; + this.keySetData = other.keySetData; + this.installerPackageName = other.installerPackageName; + this.isOrphaned = other.isOrphaned; + this.volumeUuid = other.volumeUuid; + this.categoryHint = other.categoryHint; + this.updateAvailable = other.updateAvailable; + this.verificationInfo = other.verificationInfo; + + if (mOldCodePaths != null) { + if (other.mOldCodePaths != null) { + mOldCodePaths.clear(); + mOldCodePaths.addAll(other.mOldCodePaths); + } else { + mOldCodePaths = null; + } + } + mUserState.clear(); + for (int i = 0; i < other.mUserState.size(); i++) { + mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i)); + } + return this; + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 6d242f48549e..5c88e0637092 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -539,16 +539,18 @@ public final class Settings { if((p.pkg != null) && (p.pkg.applicationInfo != null)) { p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } - mDisabledSysPackages.put(name, p); - + final PackageSetting disabled; if (replaced) { // a little trick... when we install the new package, we don't // want to modify the existing PackageSetting for the built-in - // version. so at this point we need a new PackageSetting that - // is okay to muck with. - PackageSetting newp = new PackageSetting(p); - replacePackageLPw(name, newp); + // version. so at this point we make a copy to place into the + // disabled set. + disabled = new PackageSetting(p); + } else { + disabled = p; } + mDisabledSysPackages.put(name, disabled); + return true; } return false; @@ -1105,19 +1107,6 @@ public final class Settings { mInstallerPackages.remove(packageName); } - private void replacePackageLPw(String name, PackageSetting newp) { - final PackageSetting p = mPackages.get(name); - if (p != null) { - if (p.sharedUser != null) { - p.sharedUser.removePackage(p); - p.sharedUser.addPackage(newp); - } else { - replaceUserIdLPw(p.appId, newp); - } - } - mPackages.put(name, newp); - } - private boolean addUserIdLPw(int uid, Object obj, Object name) { if (uid > Process.LAST_APPLICATION_UID) { return false; diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index c94d209892af..1a8b2af5a3a9 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -24,7 +24,6 @@ import android.util.ArraySet; import android.util.proto.ProtoOutputStream; import java.util.ArrayList; -import java.util.Collection; import java.util.List; /** @@ -69,24 +68,26 @@ public final class SharedUserSetting extends SettingBase { proto.end(token); } - void removePackage(PackageSetting packageSetting) { - if (packages.remove(packageSetting)) { - // recalculate the pkgFlags for this shared user if needed - if ((this.pkgFlags & packageSetting.pkgFlags) != 0) { - int aggregatedFlags = uidFlags; - for (PackageSetting ps : packages) { - aggregatedFlags |= ps.pkgFlags; - } - setFlags(aggregatedFlags); + boolean removePackage(PackageSetting packageSetting) { + if (!packages.remove(packageSetting)) { + return false; + } + // recalculate the pkgFlags for this shared user if needed + if ((this.pkgFlags & packageSetting.pkgFlags) != 0) { + int aggregatedFlags = uidFlags; + for (PackageSetting ps : packages) { + aggregatedFlags |= ps.pkgFlags; } - if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) { - int aggregatedPrivateFlags = uidPrivateFlags; - for (PackageSetting ps : packages) { - aggregatedPrivateFlags |= ps.pkgPrivateFlags; - } - setPrivateFlags(aggregatedPrivateFlags); + setFlags(aggregatedFlags); + } + if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) { + int aggregatedPrivateFlags = uidPrivateFlags; + for (PackageSetting ps : packages) { + aggregatedPrivateFlags |= ps.pkgPrivateFlags; } + setPrivateFlags(aggregatedPrivateFlags); } + return true; } void addPackage(PackageSetting packageSetting) { @@ -143,4 +144,16 @@ public final class SharedUserSetting extends SettingBase { } } + /** Updates all fields in this shared user setting from another. */ + public SharedUserSetting updateFrom(SharedUserSetting sharedUser) { + copyFrom(sharedUser); + this.userId = sharedUser.userId; + this.uidFlags = sharedUser.uidFlags; + this.uidPrivateFlags = sharedUser.uidPrivateFlags; + this.seInfoTargetSdkVersion = sharedUser.seInfoTargetSdkVersion; + this.packages.clear(); + this.packages.addAll(sharedUser.packages); + this.signaturesChanged = sharedUser.signaturesChanged; + return this; + } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 350d6b66c1fb..3b19beb52e2e 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3543,8 +3543,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) { return -1; - } else if (mHasFeatureLeanback && interceptAccessibilityGestureTv(keyCode, down)) { - return -1; } else if (keyCode == KeyEvent.KEYCODE_ALL_APPS) { if (!down) { mHandler.removeMessages(MSG_HANDLE_ALL_APPS); @@ -6037,6 +6035,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + // Intercept the Accessibility keychord for TV (DPAD_DOWN + Back) before the keyevent is + // processed through interceptKeyEventBeforeDispatch since Talkback may consume this event + // before it has a chance to reach that method. + if (mHasFeatureLeanback) { + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_DOWN: + case KeyEvent.KEYCODE_BACK: { + boolean handled = interceptAccessibilityGestureTv(keyCode, down); + if (handled) { + result &= ~ACTION_PASS_TO_USER; + } + break; + } + } + } + if (useHapticFeedback) { performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false, "Virtual Key - Press"); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 20918995a8ee..a4d42a127cd6 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -976,10 +976,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + looperStats.reset(); long elapsedNanos = SystemClock.elapsedRealtimeNanos(); for (LooperStats.ExportedEntry entry : entries) { - StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 9 /* fields */); - e.writeLong(0); // uid collection not implemented yet + StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 10 /* fields */); + e.writeInt(1000); // uid collection not implemented yet e.writeString(entry.handlerClassName); e.writeString(entry.threadName); e.writeString(entry.messageName); @@ -988,6 +989,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { e.writeLong(entry.recordedMessageCount); e.writeLong(entry.totalLatencyMicros); e.writeLong(entry.cpuUsageMicros); + e.writeBoolean(entry.isInteractive); pulledData.add(e); } } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 685c444d92b5..bd82553b804b 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -21,6 +21,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -732,10 +733,13 @@ public class DisplayRotation { boolean shouldUpdateOrientationListener = false; // Configure rotation suggestions. - final int showRotationSuggestions = Settings.Secure.getIntForUser(resolver, - Settings.Secure.SHOW_ROTATION_SUGGESTIONS, - Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT, - UserHandle.USER_CURRENT); + final int showRotationSuggestions = + ActivityManager.isLowRamDeviceStatic() + ? Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DISABLED + : Settings.Secure.getIntForUser(resolver, + Settings.Secure.SHOW_ROTATION_SUGGESTIONS, + Settings.Secure.SHOW_ROTATION_SUGGESTIONS_DEFAULT, + UserHandle.USER_CURRENT); if (mShowRotationSuggestions != showRotationSuggestions) { mShowRotationSuggestions = showRotationSuggestions; shouldUpdateOrientationListener = true; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 0ca0835765bf..4c71d65115f0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2588,12 +2588,32 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy) throws SecurityException { + return getActiveAdminOrCheckPermissionForCallerLocked(who, + reqPolicy, /* permission= */ null); + } + + /** + * Finds an active admin for the caller then checks {@code permission} if admin check failed. + * + * @return an active admin or {@code null} if there is no active admin but + * {@code permission} is granted + * @throws SecurityException if caller neither has an active admin nor {@code permission} + */ + @Nullable + ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked( + ComponentName who, + int reqPolicy, + @Nullable String permission) throws SecurityException { ensureLocked(); final int callingUid = mInjector.binderGetCallingUid(); ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid); if (result != null) { return result; + } else if (permission != null + && (mContext.checkCallingPermission(permission) + == PackageManager.PERMISSION_GRANTED)) { + return null; } if (who != null) { @@ -2605,7 +2625,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) { throw new SecurityException("Admin " + admin.info.getComponent() - + " does not own the device"); + + " does not own the device"); } if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) { throw new SecurityException("Admin " + admin.info.getComponent() @@ -2621,20 +2641,39 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + admin.info.getTagForPolicy(reqPolicy)); } else { throw new SecurityException("No active admin owned by uid " - + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy); + + callingUid + " for policy #" + reqPolicy); } } ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy, boolean parent) throws SecurityException { + return getActiveAdminOrCheckPermissionForCallerLocked( + who, reqPolicy, parent, /* permission= */ null); + } + + /** + * Finds an active admin for the caller then checks {@code permission} if admin check failed. + * + * @return an active admin or {@code null} if there is no active admin but + * {@code permission} is granted + * @throws SecurityException if caller neither has an active admin nor {@code permission} + */ + @Nullable + ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked( + ComponentName who, + int reqPolicy, + boolean parent, + @Nullable String permission) throws SecurityException { ensureLocked(); if (parent) { enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "call APIs on the parent profile"); } - ActiveAdmin admin = getActiveAdminForCallerLocked(who, reqPolicy); + ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked( + who, reqPolicy, permission); return parent ? admin.getParentActiveAdmin() : admin; } + /** * Find the admin for the component and userId bit of the uid, then check * the admin's uid matches the uid. @@ -4744,10 +4783,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { preN = getTargetSdk(admin.info.getPackageName(), userHandle) <= android.os.Build.VERSION_CODES.M; } else { - // Otherwise, make sure the caller has any active admin with the right policy. - admin = getActiveAdminForCallerLocked(null, - DeviceAdminInfo.USES_POLICY_RESET_PASSWORD); - preN = getTargetSdk(admin.info.getPackageName(), + // Otherwise, make sure the caller has any active admin with the right policy or + // the required permission. + admin = getActiveAdminOrCheckPermissionForCallerLocked( + null, + DeviceAdminInfo.USES_POLICY_RESET_PASSWORD, + android.Manifest.permission.RESET_PASSWORD); + // Cannot be preN if admin is null because an exception would have been + // thrown before getting here + preN = admin == null ? false : getTargetSdk(admin.info.getPackageName(), userHandle) <= android.os.Build.VERSION_CODES.M; // As of N, password resetting to empty/null is not allowed anymore. @@ -4763,9 +4807,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // As of N, password cannot be changed by the admin if it is already set. if (isLockScreenSecureUnchecked(userHandle)) { if (!preN) { - throw new SecurityException("Admin cannot change current password"); + throw new SecurityException("Cannot change current password"); } else { - Slog.e(LOG_TAG, "Admin cannot change current password"); + Slog.e(LOG_TAG, "Cannot change current password"); return false; } } @@ -5136,31 +5180,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int callingUserId = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { - // This API can only be called by an active device admin, - // so try to retrieve it to check that the caller is one. - final ActiveAdmin admin = getActiveAdminForCallerLocked( - null, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent); - + // Make sure the caller has any active admin with the right policy or + // the required permission. + final ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked( + null, + DeviceAdminInfo.USES_POLICY_FORCE_LOCK, + parent, + android.Manifest.permission.LOCK_DEVICE); final long ident = mInjector.binderClearCallingIdentity(); try { - final ComponentName adminComponent = admin.info.getComponent(); - // Evict key - if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) { - enforceManagedProfile( - callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY"); - if (!isProfileOwner(adminComponent, callingUserId)) { - throw new SecurityException("Only profile owner admins can set " - + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY"); - } - if (parent) { - throw new IllegalArgumentException( - "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent"); - } - if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) { - throw new UnsupportedOperationException( - "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices"); + final ComponentName adminComponent = admin == null ? + null : admin.info.getComponent(); + if (adminComponent != null) { + // For Profile Owners only, callers with only permission not allowed. + if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) { + // Evict key + enforceManagedProfile( + callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY"); + if (!isProfileOwner(adminComponent, callingUserId)) { + throw new SecurityException("Only profile owner admins can set " + + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY"); + } + if (parent) { + throw new IllegalArgumentException( + "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent"); + } + if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) { + throw new UnsupportedOperationException( + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices"); + } + mUserManager.evictCredentialEncryptionKey(callingUserId); } - mUserManager.evictCredentialEncryptionKey(callingUserId); } // Lock all users unless this is a managed profile with a separate challenge @@ -5179,7 +5229,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mInjector.getTrustManager().setDeviceLockedForUser(userToLock, true); } - if (SecurityLog.isLoggingEnabled()) { + if (SecurityLog.isLoggingEnabled() && adminComponent != null) { final int affectedUserId = parent ? getProfileParentId(callingUserId) : callingUserId; SecurityLog.writeEvent(SecurityLog.TAG_REMOTE_LOCK, 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 233874475d9c..f6e5601dc4f7 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -69,7 +69,6 @@ import org.mockito.invocation.InvocationOnMock; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityRecordTests extends ActivityTestsBase { - private ActivityTaskManagerService mService; private TestActivityStack mStack; private TaskRecord mTask; private ActivityRecord mActivity; @@ -79,10 +78,10 @@ public class ActivityRecordTests extends ActivityTestsBase { public void setUp() throws Exception { super.setUp(); - mService = createActivityTaskManagerService(); - mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + setupActivityTaskManagerService(); + mStack = mSupervisor.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); + mTask = new TaskBuilder(mSupervisor).setStack(mStack).build(); mActivity = new ActivityBuilder(mService).setTask(mTask).build(); } 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 1aa80c884eeb..0345a81b5bec 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java @@ -64,8 +64,6 @@ import java.util.ArrayList; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityStackSupervisorTests extends ActivityTestsBase { - private ActivityTaskManagerService mService; - private ActivityStackSupervisor mSupervisor; private ActivityStack mFullscreenStack; @Before @@ -73,8 +71,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { public void setUp() throws Exception { super.setUp(); - mService = createActivityTaskManagerService(); - mSupervisor = mService.mStackSupervisor; + setupActivityTaskManagerService(); mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } 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 95f8fd177075..ab814ee15df0 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -60,8 +60,6 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityStackTests extends ActivityTestsBase { - private ActivityTaskManagerService mService; - private ActivityStackSupervisor mSupervisor; private ActivityDisplay mDefaultDisplay; private ActivityStack mStack; private TaskRecord mTask; @@ -71,9 +69,8 @@ public class ActivityStackTests extends ActivityTestsBase { public void setUp() throws Exception { super.setUp(); - mService = createActivityTaskManagerService(); - mSupervisor = mService.mStackSupervisor; - mDefaultDisplay = mService.mStackSupervisor.getDefaultDisplay(); + setupActivityTaskManagerService(); + mDefaultDisplay = mSupervisor.getDefaultDisplay(); mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); mTask = new TaskBuilder(mSupervisor).setStack(mStack).build(); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java index d032eb51c974..749403ea4013 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java @@ -32,26 +32,32 @@ 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.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; +import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM; 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.anyObject; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityOptions; import android.app.IApplicationThread; +import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.WindowLayout; @@ -65,28 +71,6 @@ import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; import android.view.Gravity; -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED; -import static com.android.server.am.ActivityManagerService.ANIMATE; -import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING; - -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.anyObject; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.times; - import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -106,7 +90,6 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(AndroidJUnit4.class) public class ActivityStarterTests extends ActivityTestsBase { - private ActivityTaskManagerService mService; private ActivityStarter mStarter; private ActivityStartController mController; private ActivityMetricsLogger mActivityMetricsLogger; @@ -130,7 +113,7 @@ public class ActivityStarterTests extends ActivityTestsBase { @Override public void setUp() throws Exception { super.setUp(); - mService = createActivityTaskManagerService(); + setupActivityTaskManagerService(); mController = mock(ActivityStartController.class); mActivityMetricsLogger = mock(ActivityMetricsLogger.class); clearInvocations(mActivityMetricsLogger); @@ -323,7 +306,22 @@ public class ActivityStarterTests extends ActivityTestsBase { } } - private ActivityStarter prepareStarter(int launchFlags) { + private ActivityStarter prepareStarter(@Intent.Flags int launchFlags) { + return prepareStarter(launchFlags, true /* mockGetLaunchStack */); + } + + /** + * Creates a {@link ActivityStarter} with default parameters and necessary mocks. + * + * @param launchFlags The intent flags to launch activity. + * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for + * always launching to the testing stack. Set to false when allowing + * the activity can be launched to any stack that is decided by real + * implementation. + * @return A {@link ActivityStarter} with default setup. + */ + private ActivityStarter prepareStarter(@Intent.Flags int launchFlags, + boolean mockGetLaunchStack) { // always allow test to start activity. doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission( any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), @@ -343,11 +341,13 @@ public class ActivityStarterTests extends ActivityTestsBase { // return task when created. doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any()); - // direct starter to use spy stack. - doReturn(stack).when(mService.mStackSupervisor) - .getLaunchStack(any(), any(), any(), anyBoolean()); - doReturn(stack).when(mService.mStackSupervisor) - .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt()); + if (mockGetLaunchStack) { + // Direct starter to use spy stack. + doReturn(stack).when(mService.mStackSupervisor) + .getLaunchStack(any(), any(), any(), anyBoolean()); + doReturn(stack).when(mService.mStackSupervisor) + .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt()); + } // Set up mock package manager internal and make sure no unmocked methods are called PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class, @@ -546,4 +546,84 @@ public class ActivityStarterTests extends ActivityTestsBase { eq(ActivityBuilder.getDefaultComponent().getPackageName()), anyInt(), anyBoolean(), any(), eq(false)); } + + /** + * This test ensures that when starting an existing single task activity on secondary display + * which is not the top focused display, it should deliver new intent to the activity and not + * create a new stack. + */ + @Test + public void testDeliverIntentToTopActivityOfNonTopDisplay() { + final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK, + false /* mockGetLaunchStack */); + + // Create a secondary display at bottom. + final TestActivityDisplay secondaryDisplay = spy(addNewActivityDisplayAt(POSITION_BOTTOM)); + final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + + // Create an activity record on the top of secondary display. + final ComponentName componentName = ComponentName.createRelative( + DEFAULT_COMPONENT_PACKAGE_NAME, + DEFAULT_COMPONENT_PACKAGE_NAME + ".ReusableActivity"); + final TaskRecord taskRecord = new TaskBuilder(mSupervisor) + .setComponent(componentName) + .setStack(stack) + .build(); + final ActivityRecord topActivityOnSecondaryDisplay = new ActivityBuilder(mService) + .setComponent(componentName) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .setTask(taskRecord) + .build(); + + // Put an activity on default display as the top focused activity. + new ActivityBuilder(mService).setCreateTask(true).build(); + + // Start activity with the same intent as {@code topActivityOnSecondaryDisplay} + // on secondary display. + final ActivityOptions options = ActivityOptions.makeBasic() + .setLaunchDisplayId(secondaryDisplay.mDisplayId); + final int result = starter.setReason("testDeliverIntentToTopActivityOfNonTopDisplay") + .setIntent(topActivityOnSecondaryDisplay.intent) + .setActivityOptions(options.toBundle()) + .execute(); + + // Ensure result is delivering intent to top. + assertEquals(START_DELIVERED_TO_TOP, result); + + // Ensure secondary display only creates one stack. + verify(secondaryDisplay, times(1)).createStack(anyInt(), anyInt(), anyBoolean()); + } + + /** + * This test ensures that a reused top activity in the top focused stack is able to be + * reparented to another display. + */ + @Test + public void testReparentTopFocusedActivityToSecondaryDisplay() { + final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK, + false /* mockGetLaunchStack */); + + // Create a secondary display at bottom. + final TestActivityDisplay secondaryDisplay = addNewActivityDisplayAt(POSITION_BOTTOM); + secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + + // Put an activity on default display as the top focused activity. + final ActivityRecord topActivity = new ActivityBuilder(mService) + .setCreateTask(true) + .setLaunchMode(LAUNCH_SINGLE_TASK) + .build(); + + // Start activity with the same intent as {@code topActivity} on secondary display. + final ActivityOptions options = ActivityOptions.makeBasic() + .setLaunchDisplayId(secondaryDisplay.mDisplayId); + starter.setReason("testReparentTopFocusedActivityToSecondaryDisplay") + .setIntent(topActivity.intent) + .setActivityOptions(options.toBundle()) + .execute(); + + // Ensure the activity is moved to secondary display. + assertEquals(secondaryDisplay, topActivity.getDisplay()); + } } 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 bb8e5c555481..9d09f5c44a28 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -22,6 +22,7 @@ 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.Display.DEFAULT_DISPLAY; +import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; @@ -56,11 +57,14 @@ import android.content.pm.IPackageManager; import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.os.HandlerThread; import android.os.Looper; import android.os.UserHandle; import android.service.voice.IVoiceInteractionSession; import android.testing.DexmakerShareClassLoaderRule; +import android.view.Display; +import android.view.DisplayInfo; import androidx.test.InstrumentationRegistry; @@ -86,6 +90,8 @@ import java.util.List; public class ActivityTestsBase { private static boolean sOneTimeSetupDone = false; + private static int sNextDisplayId = DEFAULT_DISPLAY + 1; + @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); @@ -93,6 +99,9 @@ public class ActivityTestsBase { private final Context mContext = InstrumentationRegistry.getContext(); private HandlerThread mHandlerThread; + ActivityTaskManagerService mService; + ActivityStackSupervisor mSupervisor; + // Default package name static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo"; @@ -122,6 +131,11 @@ public class ActivityTestsBase { return atm; } + void setupActivityTaskManagerService() { + mService = createActivityTaskManagerService(); + mSupervisor = mService.mStackSupervisor; + } + ActivityManagerService createActivityManagerService() { final TestActivityTaskManagerService atm = spy(new TestActivityTaskManagerService(mContext)); @@ -134,6 +148,18 @@ public class ActivityTestsBase { return am; } + /** Creates a {@link TestActivityDisplay}. */ + TestActivityDisplay createNewActivityDisplay() { + return TestActivityDisplay.create(mSupervisor, sNextDisplayId++); + } + + /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */ + TestActivityDisplay addNewActivityDisplayAt(int position) { + final TestActivityDisplay display = createNewActivityDisplay(); + mSupervisor.addChild(display, position); + return display; + } + void setupActivityManagerService( TestActivityManagerService am, TestActivityTaskManagerService atm) { atm.setActivityManagerService(am); @@ -173,6 +199,7 @@ public class ActivityTestsBase { private boolean mCreateTask; private ActivityStack mStack; private int mActivityFlags; + private int mLaunchMode; ActivityBuilder(ActivityTaskManagerService service) { mService = service; @@ -198,6 +225,11 @@ public class ActivityTestsBase { return this; } + ActivityBuilder setLaunchMode(int launchMode) { + mLaunchMode = launchMode; + return this; + } + ActivityBuilder setStack(ActivityStack stack) { mStack = stack; return this; @@ -233,6 +265,7 @@ public class ActivityTestsBase { aInfo.applicationInfo.packageName = mComponent.getPackageName(); aInfo.applicationInfo.uid = mUid; aInfo.flags |= mActivityFlags; + aInfo.launchMode = mLaunchMode; final ActivityRecord activity = new ActivityRecord(mService, null /* caller */, 0 /* launchedFromPid */, 0, null, intent, null, @@ -500,7 +533,7 @@ public class ActivityTestsBase { @Override public void initialize() { super.initialize(); - mDisplay = spy(new TestActivityDisplay(this, DEFAULT_DISPLAY)); + mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY)); addChild(mDisplay, ActivityDisplay.POSITION_TOP); } @@ -516,10 +549,20 @@ public class ActivityTestsBase { } protected static class TestActivityDisplay extends ActivityDisplay { - private final ActivityStackSupervisor mSupervisor; - TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) { - super(supervisor, displayId); + + static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) { + if (displayId == DEFAULT_DISPLAY) { + return new TestActivityDisplay(supervisor, + supervisor.mDisplayManager.getDisplay(displayId)); + } + final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, + new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); + return new TestActivityDisplay(supervisor, display); + } + + TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) { + super(supervisor, display); // Normally this comes from display-properties as exposed by WM. Without that, just // hard-code to FULLSCREEN for tests. setWindowingMode(WINDOWING_MODE_FULLSCREEN); diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java index 519521429f1b..70cfad1d0f2f 100644 --- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java +++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java @@ -870,8 +870,8 @@ public class RecentTasksTest extends ActivityTestsBase { @Override public void initialize() { super.initialize(); - mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY); - mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY); + mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY); + mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1); addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP); addChild(mDisplay, ActivityDisplay.POSITION_TOP); } diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java index d56c6a6d7403..aa3046fb694c 100644 --- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java +++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java @@ -51,7 +51,6 @@ import java.util.ArrayList; public class RunningTasksTest extends ActivityTestsBase { private Context mContext = InstrumentationRegistry.getContext(); - private ActivityTaskManagerService mService; private RunningTasks mRunningTasks; @@ -60,21 +59,20 @@ public class RunningTasksTest extends ActivityTestsBase { public void setUp() throws Exception { super.setUp(); - mService = createActivityTaskManagerService(); + setupActivityTaskManagerService(); mRunningTasks = new RunningTasks(); } @Test public void testCollectTasksByLastActiveTime() throws Exception { // Create a number of stacks with tasks (of incrementing active time) - final ActivityStackSupervisor supervisor = mService.mStackSupervisor; final ArrayList<ActivityDisplay> displays = new ArrayList<>(); - final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY); + final ActivityDisplay display = TestActivityDisplay.create(mSupervisor, DEFAULT_DISPLAY); displays.add(display); final int numStacks = 2; for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) { - final ActivityStack stack = new TestActivityStack(display, stackIndex, supervisor, + final ActivityStack stack = new TestActivityStack(display, stackIndex, mSupervisor, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true); display.addChild(stack, POSITION_BOTTOM); } 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 ee41c0bdac82..c3c07880f605 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -716,9 +716,9 @@ public class PackageManagerSettingsTests { assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState); assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState)); assertThat(origPkgSetting.name, is(testPkgSetting.name)); - // oldCodePaths is _not_ copied - // assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths); - // assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths))); + // mOldCodePaths is _not_ copied + // assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths); + // assertThat(origPkgSetting.mOldCodePaths, is(not(testPkgSetting.mOldCodePaths))); assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName); assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName)); assertSame(origPkgSetting.pkg, testPkgSetting.pkg); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 519b3ae7bc46..4344285e31e7 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -519,8 +519,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_HIGH); NotificationRecord r = generateNotificationRecord(channel); - assertTrue(mService.isBlocked(r, mUsageStats)); - verify(mUsageStats, times(1)).registerSuspendedByAdmin(eq(r)); + + // isBlocked is only used for user blocking, not app suspension + assertFalse(mService.isBlocked(r, mUsageStats)); } @Test diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ffbe7d34e5e7..61c38fbea5c0 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1070,6 +1070,16 @@ public class CarrierConfigManager { /** * Indexes of SPN format strings in wfcSpnFormats and wfcDataSpnFormats. + * + * <p>Available options are: + * <ul> + * <li> 0: %s</li> + * <li> 1: %s Wi-Fi Calling</li> + * <li> 2: WLAN Call</li> + * <li> 3: %s WLAN Call</li> + * <li> 4: %s Wi-Fi</li> + * <li> 5: WiFi Calling | %s</li> + * <li> 6: %s VoWifi</li> * @hide */ public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int"; @@ -2272,7 +2282,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false); - sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, false); + sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, true); // MMS defaults sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false); diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java index 6bf22a05beec..8015b07fa024 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java @@ -33,6 +33,7 @@ import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.SmsConstants; import java.io.UnsupportedEncodingException; +import java.util.Locale; /** * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is @@ -44,16 +45,34 @@ public class GsmSmsCbMessage { * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5. */ private static final String[] LANGUAGE_CODES_GROUP_0 = { - "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu", - "pl", null + Locale.GERMAN.getLanguage(), // German + Locale.ENGLISH.getLanguage(), // English + Locale.ITALIAN.getLanguage(), // Italian + Locale.FRENCH.getLanguage(), // French + new Locale("es").getLanguage(), // Spanish + new Locale("nl").getLanguage(), // Dutch + new Locale("sv").getLanguage(), // Swedish + new Locale("da").getLanguage(), // Danish + new Locale("pt").getLanguage(), // Portuguese + new Locale("fi").getLanguage(), // Finnish + new Locale("nb").getLanguage(), // Norwegian + new Locale("el").getLanguage(), // Greek + new Locale("tr").getLanguage(), // Turkish + new Locale("hu").getLanguage(), // Hungarian + new Locale("pl").getLanguage(), // Polish + null }; /** * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5. */ private static final String[] LANGUAGE_CODES_GROUP_2 = { - "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null, - null, null + new Locale("cs").getLanguage(), // Czech + new Locale("he").getLanguage(), // Hebrew + new Locale("ar").getLanguage(), // Arabic + new Locale("ru").getLanguage(), // Russian + new Locale("is").getLanguage(), // Icelandic + null, null, null, null, null, null, null, null, null, null, null }; private static final char CARRIAGE_RETURN = 0x0d; diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 119f56a5013c..13c10478ba3e 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -1842,9 +1842,15 @@ class Linker { } else { // Adjust the SplitConstraints so that their SDK version is stripped if it is less than or // equal to the minSdk. + const size_t origConstraintSize = options_.split_constraints.size(); options_.split_constraints = AdjustSplitConstraintsForMinSdk(context_->GetMinSdkVersion(), options_.split_constraints); + if (origConstraintSize != options_.split_constraints.size()) { + context_->GetDiagnostics()->Warn(DiagMessage() + << "requested to split resources prior to min sdk of " + << context_->GetMinSdkVersion()); + } TableSplitter table_splitter(options_.split_constraints, options_.table_splitter_options); if (!table_splitter.VerifySplitConstraints(context_)) { return 1; diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp index c6c82b04ff2e..5862d31e3f94 100644 --- a/tools/aapt2/cmd/Util.cpp +++ b/tools/aapt2/cmd/Util.cpp @@ -73,6 +73,7 @@ bool ParseSplitParameter(const StringPiece& arg, IDiagnostics* diag, std::string } *out_path = parts[0]; + out_split->name = parts[1]; for (const StringPiece& config_str : util::Tokenize(parts[1], ',')) { ConfigDescription config; if (!ConfigDescription::Parse(config_str, &config)) { @@ -119,12 +120,15 @@ std::vector<SplitConstraints> AdjustSplitConstraintsForMinSdk( for (const SplitConstraints& constraints : split_constraints) { SplitConstraints constraint; for (const ConfigDescription& config : constraints.configs) { - if (config.sdkVersion <= min_sdk) { - constraint.configs.insert(config.CopyWithoutSdkVersion()); - } else { - constraint.configs.insert(config); + const ConfigDescription &configToInsert = (config.sdkVersion <= min_sdk) + ? config.CopyWithoutSdkVersion() + : config; + // only add the config if it actually selects something + if (configToInsert != ConfigDescription::DefaultConfig()) { + constraint.configs.insert(configToInsert); } } + constraint.name = constraints.name; adjusted_constraints.push_back(std::move(constraint)); } return adjusted_constraints; diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp index b9fb5b2868a7..158ef299f6e2 100644 --- a/tools/aapt2/cmd/Util_test.cpp +++ b/tools/aapt2/cmd/Util_test.cpp @@ -22,6 +22,10 @@ #include "test/Test.h" namespace aapt { +#define EXPECT_CONFIG_EQ(constraints, config) \ + EXPECT_EQ(constraints.configs.size(), 1); \ + EXPECT_EQ(*constraints.configs.begin(), config); \ + constraints.configs.clear(); TEST(UtilTest, SplitNamesAreSanitized) { AppInfo app_info{"com.pkg"}; @@ -84,4 +88,287 @@ TEST (UtilTest, LongVersionCodeUndefined) { EXPECT_EQ(compiled_version_code_major->value.data, 0x61); } + +TEST (UtilTest, ParseSplitParameter) { + IDiagnostics* diagnostics = test::ContextBuilder().Build().get()->GetDiagnostics(); + std::string path; + SplitConstraints constraints; + ConfigDescription expected_configuration; + + // ========== Test IMSI ========== + // mcc: 'mcc[0-9]{3}' + // mnc: 'mnc[0-9]{1,3}' + ASSERT_TRUE(ParseSplitParameter(":mcc310", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setMcc(0x0136) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc004", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setMcc(0x0136) + .setMnc(0x0004) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc000", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setMcc(0x0136) + .setMnc(0xFFFF) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + // ========== Test LOCALE ========== + // locale: '[a-z]{2,3}(-r[a-z]{2})?' + // locale: 'b+[a-z]{2,3}(+[a-z[0-9]]{2})?' + ASSERT_TRUE(ParseSplitParameter(":es", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setLanguage(0x6573) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":fr-rCA", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setLanguage(0x6672) + .setCountry(0x4341) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":b+es+419", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setLanguage(0x6573) + .setCountry(0xA424) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + // ========== Test SCREEN_TYPE ========== + // orientation: '(port|land|square)' + // touchscreen: '(notouch|stylus|finger)' + // density" '(anydpi|nodpi|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|[0-9]*dpi)' + ASSERT_TRUE(ParseSplitParameter(":square", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setOrientation(0x03) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":stylus", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setTouchscreen(0x02) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":xxxhdpi", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setDensity(0x0280) + .setSdkVersion(0x0004) // version [any density requires donut] + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":land-xhdpi-finger", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setOrientation(0x02) + .setTouchscreen(0x03) + .setDensity(0x0140) + .setSdkVersion(0x0004) // version [any density requires donut] + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + // ========== Test INPUT ========== + // keyboard: '(nokeys|qwerty|12key)' + // navigation: '(nonav|dpad|trackball|wheel)' + // inputFlags: '(keysexposed|keyshidden|keyssoft)' + // inputFlags: '(navexposed|navhidden)' + ASSERT_TRUE(ParseSplitParameter(":qwerty", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setKeyboard(0x02) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":dpad", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setNavigation(0x02) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":keyssoft-navhidden", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setInputFlags(0x0B) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":keyshidden-nokeys-navexposed-trackball", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setKeyboard(0x01) + .setNavigation(0x03) + .setInputFlags(0x06) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + // ========== Test SCREEN_SIZE ========== + // screenWidth/screenHeight: '[0-9]+x[0-9]+' + ASSERT_TRUE(ParseSplitParameter(":1920x1080", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setScreenWidth(0x0780) + .setScreenHeight(0x0438) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + // ========== Test VERSION ========== + // version 'v[0-9]+' + + // ========== Test SCREEN_CONFIG ========== + // screenLayout [direction]: '(ldltr|ldrtl)' + // screenLayout [size]: '(small|normal|large|xlarge)' + // screenLayout [long]: '(long|notlong)' + // uiMode [type]: '(desk|car|television|appliance|watch|vrheadset)' + // uiMode [night]: '(night|notnight)' + // smallestScreenWidthDp: 'sw[0-9]dp' + ASSERT_TRUE(ParseSplitParameter(":ldrtl", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setScreenLayout(0x80) + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":small", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setScreenLayout(0x01) + .setSdkVersion(0x0004) // screenLayout (size) requires donut + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":notlong", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setScreenLayout(0x10) + .setSdkVersion(0x0004) // screenLayout (long) requires donut + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":ldltr-normal-long", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setScreenLayout(0x62) + .setSdkVersion(0x0004) // screenLayout (size|long) requires donut + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":car", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setUiMode(0x03) + .setSdkVersion(0x0008) // uiMode requires froyo + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":vrheadset", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setUiMode(0x07) + .setSdkVersion(0x001A) // uiMode 'vrheadset' requires oreo + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":television-night", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setUiMode(0x24) + .setSdkVersion(0x0008) // uiMode requires froyo + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":sw1920dp", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setSmallestScreenWidthDp(0x0780) + .setSdkVersion(0x000D) // smallestScreenWidthDp requires honeycomb mr2 + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + // ========== Test SCREEN_SIZE_DP ========== + // screenWidthDp: 'w[0-9]dp' + // screenHeightDp: 'h[0-9]dp' + ASSERT_TRUE(ParseSplitParameter(":w1920dp", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setScreenWidthDp(0x0780) + .setSdkVersion(0x000D) // screenWidthDp requires honeycomb mr2 + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":h1080dp", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setScreenHeightDp(0x0438) + .setSdkVersion(0x000D) // screenHeightDp requires honeycomb mr2 + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + // ========== Test SCREEN_CONFIG_2 ========== + // screenLayout2: '(round|notround)' + // colorMode: '(widecg|nowidecg)' + // colorMode: '(highhdr|lowdr)' + ASSERT_TRUE(ParseSplitParameter(":round", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setScreenLayout2(0x02) + .setSdkVersion(0x0017) // screenLayout2 (round) requires marshmallow + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); + + ASSERT_TRUE(ParseSplitParameter(":widecg-highdr", + diagnostics, &path, &constraints)); + expected_configuration = test::ConfigDescriptionBuilder() + .setColorMode(0x0A) + .setSdkVersion(0x001A) // colorMode (hdr|colour gamut) requires oreo + .Build(); + EXPECT_CONFIG_EQ(constraints, expected_configuration); +} + +TEST (UtilTest, AdjustSplitConstraintsForMinSdk) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + + IDiagnostics* diagnostics = context.get()->GetDiagnostics(); + std::vector<SplitConstraints> test_constraints; + std::string path; + + test_constraints.push_back({}); + ASSERT_TRUE(ParseSplitParameter(":v7", + diagnostics, &path, &test_constraints.back())); + test_constraints.push_back({}); + ASSERT_TRUE(ParseSplitParameter(":xhdpi", + diagnostics, &path, &test_constraints.back())); + EXPECT_EQ(test_constraints.size(), 2); + EXPECT_EQ(test_constraints[0].name, "v7"); + EXPECT_EQ(test_constraints[0].configs.size(), 1); + EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig()); + EXPECT_EQ(test_constraints[1].name, "xhdpi"); + EXPECT_EQ(test_constraints[1].configs.size(), 1); + EXPECT_NE(*test_constraints[0].configs.begin(), ConfigDescription::DefaultConfig()); + + auto adjusted_contraints = AdjustSplitConstraintsForMinSdk(26, test_constraints); + EXPECT_EQ(adjusted_contraints.size(), 2); + EXPECT_EQ(adjusted_contraints[0].name, "v7"); + EXPECT_EQ(adjusted_contraints[0].configs.size(), 0); + EXPECT_EQ(adjusted_contraints[1].name, "xhdpi"); + EXPECT_EQ(adjusted_contraints[1].configs.size(), 1); + EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig()); +} + } // namespace aapt diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp index e99174302d26..b5c330622c72 100644 --- a/tools/aapt2/split/TableSplitter.cpp +++ b/tools/aapt2/split/TableSplitter.cpp @@ -154,6 +154,12 @@ static void MarkNonPreferredDensitiesAsClaimed( bool TableSplitter::VerifySplitConstraints(IAaptContext* context) { bool error = false; for (size_t i = 0; i < split_constraints_.size(); i++) { + if (split_constraints_[i].configs.size() == 0) { + // For now, treat this as a warning. We may consider aborting processing. + context->GetDiagnostics()->Warn(DiagMessage() + << "no configurations for constraint '" + << split_constraints_[i].name << "'"); + } for (size_t j = i + 1; j < split_constraints_.size(); j++) { for (const ConfigDescription& config : split_constraints_[i].configs) { if (split_constraints_[j].configs.find(config) != split_constraints_[j].configs.end()) { diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h index 6aec2576261e..ed24bc3991b4 100644 --- a/tools/aapt2/split/TableSplitter.h +++ b/tools/aapt2/split/TableSplitter.h @@ -30,6 +30,7 @@ namespace aapt { struct SplitConstraints { std::set<ConfigDescription> configs; + std::string name; }; struct TableSplitterOptions { diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index fd5262af6e48..be6e51050c86 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -204,6 +204,112 @@ class PostProcessingConfigurationBuilder { configuration::PostProcessingConfiguration config_; }; +class ConfigDescriptionBuilder { + public: + ConfigDescriptionBuilder() = default; + + ConfigDescriptionBuilder& setMcc(uint16_t mcc) { + config_.mcc = mcc; + return *this; + } + ConfigDescriptionBuilder& setMnc(uint16_t mnc) { + config_.mnc = mnc; + return *this; + } + ConfigDescriptionBuilder& setLanguage(uint16_t language) { + config_.language[0] = language >> 8; + config_.language[1] = language & 0xff; + return *this; + } + ConfigDescriptionBuilder& setCountry(uint16_t country) { + config_.country[0] = country >> 8; + config_.country[1] = country & 0xff; + return *this; + } + ConfigDescriptionBuilder& setOrientation(uint8_t orientation) { + config_.orientation = orientation; + return *this; + } + ConfigDescriptionBuilder& setTouchscreen(uint8_t touchscreen) { + config_.touchscreen = touchscreen; + return *this; + } + ConfigDescriptionBuilder& setDensity(uint16_t density) { + config_.density = density; + return *this; + } + ConfigDescriptionBuilder& setKeyboard(uint8_t keyboard) { + config_.keyboard = keyboard; + return *this; + } + ConfigDescriptionBuilder& setNavigation(uint8_t navigation) { + config_.navigation = navigation; + return *this; + } + ConfigDescriptionBuilder& setInputFlags(uint8_t inputFlags) { + config_.inputFlags = inputFlags; + return *this; + } + ConfigDescriptionBuilder& setInputPad0(uint8_t inputPad0) { + config_.inputPad0 = inputPad0; + return *this; + } + ConfigDescriptionBuilder& setScreenWidth(uint16_t screenWidth) { + config_.screenWidth = screenWidth; + return *this; + } + ConfigDescriptionBuilder& setScreenHeight(uint16_t screenHeight) { + config_.screenHeight = screenHeight; + return *this; + } + ConfigDescriptionBuilder& setSdkVersion(uint16_t sdkVersion) { + config_.sdkVersion = sdkVersion; + return *this; + } + ConfigDescriptionBuilder& setMinorVersion(uint16_t minorVersion) { + config_.minorVersion = minorVersion; + return *this; + } + ConfigDescriptionBuilder& setScreenLayout(uint8_t screenLayout) { + config_.screenLayout = screenLayout; + return *this; + } + ConfigDescriptionBuilder& setUiMode(uint8_t uiMode) { + config_.uiMode = uiMode; + return *this; + } + ConfigDescriptionBuilder& setSmallestScreenWidthDp(uint16_t smallestScreenWidthDp) { + config_.smallestScreenWidthDp = smallestScreenWidthDp; + return *this; + } + ConfigDescriptionBuilder& setScreenWidthDp(uint16_t screenWidthDp) { + config_.screenWidthDp = screenWidthDp; + return *this; + } + ConfigDescriptionBuilder& setScreenHeightDp(uint16_t screenHeightDp) { + config_.screenHeightDp = screenHeightDp; + return *this; + } + ConfigDescriptionBuilder& setScreenLayout2(uint8_t screenLayout2) { + config_.screenLayout2 = screenLayout2; + return *this; + } + ConfigDescriptionBuilder& setColorMode(uint8_t colorMode) { + config_.colorMode = colorMode; + return *this; + } + ConfigDescriptionBuilder& setScreenConfigPad2(uint16_t screenConfigPad2) { + config_.screenConfigPad2 = screenConfigPad2; + return *this; + } + ConfigDescription Build() { + return config_; + } + + private: + ConfigDescription config_; +}; + } // namespace test } // namespace aapt |