diff options
168 files changed, 4413 insertions, 5604 deletions
diff --git a/Android.bp b/Android.bp index 9cc17ead3491..c1949295a0d8 100644 --- a/Android.bp +++ b/Android.bp @@ -752,14 +752,26 @@ java_library { name: "framework", defaults: ["framework-defaults"], javac_shard_size: 150, + required: [ + "framework-platform-compat-config", + "libcore-platform-compat-config", + ], } java_library { name: "framework-annotation-proc", defaults: ["framework-defaults"], installable: false, - // Use UsedByApps annotation processor - plugins: ["unsupportedappusage-annotation-processor"], + plugins: [ + "unsupportedappusage-annotation-processor", + "compat-changeid-annotation-processor", + ], +} + +platform_compat_config { + name: "framework-platform-compat-config", + prefix: "framework", + src: ":framework-annotation-proc", } // A library including just UnsupportedAppUsage.java classes. @@ -1272,11 +1284,6 @@ stubs_defaults { srcs_lib: "framework", srcs_lib_whitelist_dirs: frameworks_base_subdirs, srcs_lib_whitelist_pkgs: packages_to_document, - libs: [ - "ext", - "framework", - "voip-common", - ], local_sourcepaths: frameworks_base_subdirs, installable: false, annotations_enabled: true, diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 543f0edbf835..e731138ff40d 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -9,6 +9,8 @@ hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/c hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} +ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES} + owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$" shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/" diff --git a/api/current.txt b/api/current.txt index 015874d6e30c..188258bccc42 100755 --- a/api/current.txt +++ b/api/current.txt @@ -11361,6 +11361,9 @@ package android.content.pm { field public static final String FEATURE_SENSOR_RELATIVE_HUMIDITY = "android.hardware.sensor.relative_humidity"; field public static final String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter"; field public static final String FEATURE_SENSOR_STEP_DETECTOR = "android.hardware.sensor.stepdetector"; + field public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese"; + field public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd"; + field public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc"; field public static final String FEATURE_SIP = "android.software.sip"; field public static final String FEATURE_SIP_VOIP = "android.software.sip.voip"; field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore"; @@ -37412,7 +37415,7 @@ package android.provider { field public static final String CONTENT_ID = "cid"; field public static final String CONTENT_LOCATION = "cl"; field public static final String CONTENT_TYPE = "ct"; - field public static final android.net.Uri CONTENT_URI; + field @NonNull public static final android.net.Uri CONTENT_URI; field public static final String CT_START = "ctt_s"; field public static final String CT_TYPE = "ctt_t"; field public static final String FILENAME = "fn"; @@ -42104,6 +42107,7 @@ package android.telephony { public class CarrierConfigManager { method @Nullable public android.os.PersistableBundle getConfig(); + method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(String, int); method @Nullable public android.os.PersistableBundle getConfigForSubId(int); method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle); method public void notifyConfigChangedForSubId(int); @@ -42281,6 +42285,10 @@ package android.telephony { field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool"; } + public static final class CarrierConfigManager.Ims { + field public static final String KEY_PREFIX = "ims."; + } + public abstract class CellIdentity implements android.os.Parcelable { method public int describeContents(); method @Nullable public CharSequence getOperatorAlphaLong(); @@ -42463,6 +42471,7 @@ package android.telephony { method public int getBitErrorRate(); method public int getDbm(); method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel(); + method public int getRssi(); method public int getTimingAdvance(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR; @@ -70466,7 +70475,7 @@ package java.util.logging { method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String); method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @NonNull java.util.function.Supplier<java.lang.String>); method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Object); - method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, Object[]); + method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Object[]); method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Throwable); method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable Throwable, @NonNull java.util.function.Supplier<java.lang.String>); method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String); diff --git a/api/removed.txt b/api/removed.txt index f40b14614323..079543887c78 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -278,7 +278,7 @@ package android.hardware { package android.icu.util { public class JapaneseCalendar extends android.icu.util.GregorianCalendar { - field public static final int CURRENT_ERA; + field @Deprecated public static final int CURRENT_ERA; } } diff --git a/api/system-current.txt b/api/system-current.txt index 593b45db5809..9200ce579aff 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -7424,6 +7424,7 @@ package android.telephony.ims.feature { public abstract class ImsFeature { ctor public ImsFeature(); method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method public final int getSlotIndex(); method public abstract void onFeatureReady(); method public abstract void onFeatureRemoved(); method public final void setFeatureState(int); @@ -7437,7 +7438,7 @@ package android.telephony.ims.feature { field public static final int STATE_UNAVAILABLE = 0; // 0x0 } - @Deprecated public static class ImsFeature.Capabilities { + public static class ImsFeature.Capabilities { field @Deprecated protected int mCapabilities; } diff --git a/api/test-current.txt b/api/test-current.txt index 144e5f5c8f53..7ea9a26d5320 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1521,6 +1521,11 @@ package android.telephony { method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int); } + public class PhoneNumberUtils { + method public static int getMinMatchForTest(); + method public static void setMinMatchForTest(int); + } + public class ServiceState implements android.os.Parcelable { method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo); method public void setCdmaSystemAndNetworkId(int, int); diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp index e17f735b93a4..3e5877b05708 100644 --- a/cmds/bootanimation/Android.bp +++ b/cmds/bootanimation/Android.bp @@ -40,24 +40,6 @@ cc_binary { "audioplay.cpp", ], - product_variables: { - product_is_iot: { - shared_libs: [ - "libandroidthings", - "libchrome", - ], - srcs: [ - "iot/iotbootanimation_main.cpp", - "iot/BootAction.cpp", - "iot/BootParameters.cpp", - ], - exclude_srcs: [ - "bootanimation_main.cpp", - "audioplay.cpp", - ], - }, - }, - init_rc: ["bootanim.rc"], } @@ -77,10 +59,4 @@ cc_library_shared { "libGLESv1_CM", "libgui", ], - - product_variables: { - product_is_iot: { - init_rc: ["iot/bootanim_iot.rc"], - }, - }, } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index ed6c25dc49c3..95bdc4a79af9 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -63,6 +63,10 @@ #include "BootAnimation.h" +#define ANIM_PATH_MAX 255 +#define STR(x) #x +#define STRTO(x) STR(x) + namespace android { static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip"; @@ -94,7 +98,7 @@ static constexpr size_t FONT_NUM_ROWS = FONT_NUM_CHARS / FONT_NUM_COLS; static const int TEXT_CENTER_VALUE = INT_MAX; static const int TEXT_MISSING_VALUE = INT_MIN; static const char EXIT_PROP_NAME[] = "service.bootanim.exit"; -static const int ANIM_ENTRY_NAME_MAX = 256; +static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1; static constexpr size_t TEXT_POS_LEN_MAX = 16; // --------------------------------------------------------------------------- @@ -658,7 +662,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) animation.width = width; animation.height = height; animation.fps = fps; - } else if (sscanf(l, " %c %d %d %s #%6s %16s %16s", + } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s", &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) { //ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s", // pathType, count, pause, path, color, clockPos1, clockPos2); diff --git a/cmds/bootanimation/iot/BootAction.cpp b/cmds/bootanimation/iot/BootAction.cpp deleted file mode 100644 index fa797444d569..000000000000 --- a/cmds/bootanimation/iot/BootAction.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "BootAction.h" - -#define LOG_TAG "BootAction" - -#include <dlfcn.h> - -#include <pio/peripheral_manager_client.h> -#include <utils/Log.h> - -namespace android { - -BootAction::~BootAction() { - if (mLibHandle != nullptr) { - dlclose(mLibHandle); - } -} - -bool BootAction::init(const std::string& libraryPath, - const std::vector<ABootActionParameter>& parameters) { - APeripheralManagerClient* client = nullptr; - ALOGD("Connecting to peripheralmanager"); - // Wait for peripheral manager to come up. - while (client == nullptr) { - client = APeripheralManagerClient_new(); - if (client == nullptr) { - ALOGV("peripheralmanager is not up, sleeping before we check again."); - usleep(250000); - } - } - ALOGD("Peripheralmanager is up."); - APeripheralManagerClient_delete(client); - - - ALOGI("Loading boot action %s", libraryPath.c_str()); - mLibHandle = dlopen(libraryPath.c_str(), RTLD_NOW); - if (mLibHandle == nullptr) { - ALOGE("Unable to load library at %s :: %s", - libraryPath.c_str(), dlerror()); - return false; - } - - void* loaded = nullptr; - if (!loadSymbol("boot_action_init", &loaded) || loaded == nullptr) { - return false; - } - mLibInit = reinterpret_cast<libInit>(loaded); - - loaded = nullptr; - if (!loadSymbol("boot_action_shutdown", &loaded) || loaded == nullptr) { - return false; - } - mLibShutdown = reinterpret_cast<libShutdown>(loaded); - - // StartPart is considered optional, if it isn't exported by the library - // we will still call init and shutdown. - loaded = nullptr; - if (!loadSymbol("boot_action_start_part", &loaded) || loaded == nullptr) { - ALOGI("No boot_action_start_part found, action will not be told when " - "Animation parts change."); - } else { - mLibStartPart = reinterpret_cast<libStartPart>(loaded); - } - - ALOGD("Entering boot_action_init"); - bool result = mLibInit(parameters.data(), parameters.size()); - ALOGD("Returned from boot_action_init"); - return result; -} - -void BootAction::startPart(int partNumber, int playNumber) { - if (mLibStartPart == nullptr) return; - - ALOGD("Entering boot_action_start_part"); - mLibStartPart(partNumber, playNumber); - ALOGD("Returned from boot_action_start_part"); -} - -void BootAction::shutdown() { - ALOGD("Entering boot_action_shutdown"); - mLibShutdown(); - ALOGD("Returned from boot_action_shutdown"); -} - -bool BootAction::loadSymbol(const char* symbol, void** loaded) { - *loaded = dlsym(mLibHandle, symbol); - if (loaded == nullptr) { - ALOGE("Unable to load symbol : %s :: %s", symbol, dlerror()); - return false; - } - return true; -} - -} // namespace android diff --git a/cmds/bootanimation/iot/BootAction.h b/cmds/bootanimation/iot/BootAction.h deleted file mode 100644 index 5e2495fe6c51..000000000000 --- a/cmds/bootanimation/iot/BootAction.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _BOOTANIMATION_BOOTACTION_H -#define _BOOTANIMATION_BOOTACTION_H - -#include <string> -#include <vector> - -#include <boot_action/boot_action.h> // libandroidthings native API. -#include <utils/RefBase.h> - -namespace android { - -class BootAction : public RefBase { -public: - ~BootAction(); - - // libraryPath is a fully qualified path to the target .so library. - bool init(const std::string& libraryPath, - const std::vector<ABootActionParameter>& parameters); - - // The animation is going to start playing partNumber for the playCount'th - // time, update the action as needed. - // This is run in the same thread as the boot animation, - // you must not block here. - void startPart(int partNumber, int playCount); - - // Shutdown the boot action, this will be called shortly before the - // process is shut down to allow time for cleanup. - void shutdown(); - -private: - typedef bool (*libInit)(const ABootActionParameter* parameters, - size_t num_parameters); - typedef void (*libStartPart)(int partNumber, int playNumber); - typedef void (*libShutdown)(); - - bool loadSymbol(const char* symbol, void** loaded); - - void* mLibHandle = nullptr; - libInit mLibInit = nullptr; - libStartPart mLibStartPart = nullptr; - libShutdown mLibShutdown = nullptr; -}; - -} // namespace android - - -#endif // _BOOTANIMATION_BOOTACTION_H diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp deleted file mode 100644 index da6ad0d1f08f..000000000000 --- a/cmds/bootanimation/iot/BootParameters.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "BootParameters.h" - -#define LOG_TAG "BootParameters" - -#include <fcntl.h> - -#include <string> - -#include <android-base/file.h> -#include <base/json/json_parser.h> -#include <base/json/json_reader.h> -#include <base/json/json_value_converter.h> -#include <utils/Log.h> - -using android::base::RemoveFileIfExists; -using android::base::ReadFileToString; -using base::JSONReader; -using base::JSONValueConverter; -using base::Value; - -namespace android { - -namespace { - -// Brightness and volume are stored as integer strings in next_boot.json. -// They are divided by this constant to produce the actual float values in -// range [0.0, 1.0]. This constant must match its counterpart in -// DeviceManager. -constexpr const float kFloatScaleFactor = 1000.0f; - -constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json"; -constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json"; - -void swapBootConfigs() { - // rename() will fail if next_boot.json doesn't exist, so delete - // last_boot.json manually first. - std::string err; - if (!RemoveFileIfExists(kLastBootFile, &err)) - ALOGE("Unable to delete last boot file: %s", err.c_str()); - - if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT) - ALOGE("Unable to swap boot files: %s", strerror(errno)); - - int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE); - if (fd == -1) { - ALOGE("Unable to create next boot file: %s", strerror(errno)); - } else { - // Make next_boot.json writable to everyone so DeviceManagementService - // can save saved_parameters there. - if (fchmod(fd, DEFFILEMODE)) - ALOGE("Unable to set next boot file permissions: %s", strerror(errno)); - close(fd); - } -} - -} // namespace - -BootParameters::SavedBootParameters::SavedBootParameters() - : brightness(-kFloatScaleFactor), volume(-kFloatScaleFactor) {} - -void BootParameters::SavedBootParameters::RegisterJSONConverter( - JSONValueConverter<SavedBootParameters>* converter) { - converter->RegisterIntField("brightness", &SavedBootParameters::brightness); - converter->RegisterIntField("volume", &SavedBootParameters::volume); - converter->RegisterRepeatedString("param_names", - &SavedBootParameters::param_names); - converter->RegisterRepeatedString("param_values", - &SavedBootParameters::param_values); -} - -BootParameters::BootParameters() { - swapBootConfigs(); - loadParameters(); -} - -void BootParameters::loadParameters() { - std::string contents; - if (!ReadFileToString(kLastBootFile, &contents)) { - if (errno != ENOENT) - ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno)); - - return; - } - - std::unique_ptr<Value> json = JSONReader::Read(contents); - if (json.get() == nullptr) { - return; - } - - JSONValueConverter<SavedBootParameters> converter; - if (converter.Convert(*(json.get()), &mRawParameters)) { - mBrightness = mRawParameters.brightness / kFloatScaleFactor; - mVolume = mRawParameters.volume / kFloatScaleFactor; - - if (mRawParameters.param_names.size() == mRawParameters.param_values.size()) { - for (size_t i = 0; i < mRawParameters.param_names.size(); i++) { - mParameters.push_back({ - .key = mRawParameters.param_names[i]->c_str(), - .value = mRawParameters.param_values[i]->c_str() - }); - } - } else { - ALOGW("Parameter names and values size mismatch"); - } - } -} - -} // namespace android diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h deleted file mode 100644 index c10bd44bc2ca..000000000000 --- a/cmds/bootanimation/iot/BootParameters.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _BOOTANIMATION_BOOT_PARAMETERS_H_ -#define _BOOTANIMATION_BOOT_PARAMETERS_H_ - -#include <list> -#include <vector> - -#include <base/json/json_value_converter.h> -#include <boot_action/boot_action.h> // libandroidthings native API. - -namespace android { - -// Provides access to the parameters set by DeviceManager.reboot(). -class BootParameters { -public: - // Constructor loads the parameters for this boot and swaps the param files - // to clear the parameters for next boot. - BootParameters(); - - // Returns true if volume/brightness were explicitly set on reboot. - bool hasVolume() const { return mVolume >= 0; } - bool hasBrightness() const { return mBrightness >= 0; } - - // Returns volume/brightness in [0,1], or -1 if unset. - float getVolume() const { return mVolume; } - float getBrightness() const { return mBrightness; } - - // Returns the additional boot parameters that were set on reboot. - const std::vector<ABootActionParameter>& getParameters() const { return mParameters; } - -private: - // Raw boot saved_parameters loaded from .json. - struct SavedBootParameters { - int brightness; - int volume; - std::vector<std::unique_ptr<std::string>> param_names; - std::vector<std::unique_ptr<std::string>> param_values; - - SavedBootParameters(); - static void RegisterJSONConverter( - ::base::JSONValueConverter<SavedBootParameters>* converter); - }; - - void loadParameters(); - - float mVolume = -1.f; - float mBrightness = -1.f; - std::vector<ABootActionParameter> mParameters; - - // ABootActionParameter is just a raw pointer so we need to keep the - // original strings around to avoid losing them. - SavedBootParameters mRawParameters; -}; - -} // namespace android - - -#endif // _BOOTANIMATION_BOOT_PARAMETERS_H_ diff --git a/cmds/bootanimation/iot/bootanim_iot.rc b/cmds/bootanimation/iot/bootanim_iot.rc deleted file mode 100644 index 2fc13364b53d..000000000000 --- a/cmds/bootanimation/iot/bootanim_iot.rc +++ /dev/null @@ -1,2 +0,0 @@ -on post-fs-data - mkdir /data/misc/bootanimation 0777 root root diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp deleted file mode 100644 index 00cef430135e..000000000000 --- a/cmds/bootanimation/iot/iotbootanimation_main.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "IotBootAnimation" - -#include <base/files/file_util.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> -#include <binder/ProcessState.h> -#include <cutils/properties.h> -#include <sys/resource.h> -#include <utils/Log.h> -#include <utils/threads.h> -#include <BootAnimation.h> - -#include "BootAction.h" -#include "BootAnimationUtil.h" -#include "BootParameters.h" - -using namespace android; - -// Create a typedef for readability. -typedef android::BootAnimation::Animation Animation; - -namespace { - -constexpr const char* kDefaultLibName = "libbootaction.so"; - -class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks { -public: - BootActionAnimationCallbacks(std::unique_ptr<BootParameters> bootParameters) - : mBootParameters(std::move(bootParameters)) {} - - void init(const Vector<Animation::Part>&) override { - std::string library_path("/oem/lib/"); - - // This value is optionally provided by the user and will be written to - // /oem/oem.prop. - char property[PROP_VALUE_MAX] = {0}; - property_get("ro.oem.bootactions.lib", property, kDefaultLibName); - library_path += property; - - if (!::base::PathExists(::base::FilePath(library_path))) { - ALOGI("Skipping boot actions: %s does not exist", library_path.c_str()); - return; - } - - mBootAction = new BootAction(); - if (!mBootAction->init(library_path, mBootParameters->getParameters())) { - mBootAction = NULL; - } - }; - - void playPart(int partNumber, const Animation::Part&, int playNumber) override { - if (mBootAction != nullptr) { - mBootAction->startPart(partNumber, playNumber); - } - }; - - void shutdown() override { - if (mBootAction != nullptr) { - // If we have a bootaction we want to wait until we are actually - // told to shut down. If the animation exits early keep the action - // running. - char value[PROPERTY_VALUE_MAX] = {0}; - for (int exitRequested = 0; exitRequested == 0; ) { - property_get("service.bootanim.exit", value, "0"); - exitRequested = atoi(value); - - // Poll value at 10hz. - if (exitRequested == 0) { - usleep(100000); - } - } - - mBootAction->shutdown(); - // Give it two seconds to shut down. - sleep(2); - mBootAction = nullptr; - } - }; - -private: - std::unique_ptr<BootParameters> mBootParameters; - sp<BootAction> mBootAction = nullptr; -}; - -} // namespace - -int main() { - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY); - - // Clear our params for next boot no matter what. - std::unique_ptr<BootParameters> bootParameters(new BootParameters()); - - if (bootAnimationDisabled()) { - ALOGI("boot animation disabled"); - return 0; - } - - waitForSurfaceFlinger(); - - sp<ProcessState> proc(ProcessState::self()); - ProcessState::self()->startThreadPool(); - - sp<BootAnimation> boot = new BootAnimation( - new BootActionAnimationCallbacks(std::move(bootParameters))); - - IPCThreadState::self()->joinThreadPool(); - return 0; -} diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp index d69dd79555a1..847dda3df91f 100644 --- a/cmds/idmap/scan.cpp +++ b/cmds/idmap/scan.cpp @@ -9,7 +9,6 @@ #include <androidfw/ResourceTypes.h> #include <androidfw/StreamingZipInflater.h> #include <androidfw/ZipFileRO.h> -#include <cutils/jstring.h> #include <cutils/properties.h> #include <private/android_filesystem_config.h> // for AID_SYSTEM #include <utils/SortedVector.h> @@ -84,15 +83,9 @@ namespace { } bool check_property(String16 property, String16 value) { - const char *prop; - const char *val; - - prop = strndup16to8(property.string(), property.size()); char propBuf[PROPERTY_VALUE_MAX]; - property_get(prop, propBuf, NULL); - val = strndup16to8(value.string(), value.size()); - - return (strcmp(propBuf, val) == 0); + property_get(String8(property).c_str(), propBuf, NULL); + return String8(value) == propBuf; } int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name, diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 1254f1a3afa0..f9b6219747f7 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -2871,6 +2871,9 @@ message NetworkDnsEventReported { // Additional pass-through fields opaque to statsd. // The DNS resolver Mainline module can add new fields here without requiring an OS update. optional android.stats.dnsresolver.DnsQueryEvents dns_query_events = 8 [(log_mode) = MODE_BYTES]; + + // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom. + optional int32 sampling_rate_denom = 9; } /** diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index b29e979b5236..8233eeec06c7 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -35,8 +35,14 @@ void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; } // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently StatsPuller::StatsPuller(const int tagId) : mTagId(tagId) { - mCoolDownNs = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownNs; - VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs); + auto pullAtomInfo = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId); + if (pullAtomInfo != StatsPullerManagerImpl::kAllPullAtomInfo.end()) { + mCoolDownNs = pullAtomInfo->second.coolDownNs; + VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs); + } else { + mCoolDownNs = 0; + VLOG("Creating puller for a non-recognised tag %d.", mTagId); + } } bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) { diff --git a/config/boot-profile.txt b/config/boot-profile.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/config/boot-profile.txt diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d9c82ea31537..6c67191eb3eb 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -703,6 +703,8 @@ public final class ActivityThread extends ClientTransactionHandler { boolean autofillCompatibilityEnabled; + long[] disabledCompatChanges; + public String toString() { return "AppBindData{appInfo=" + appInfo + "}"; } @@ -920,7 +922,8 @@ public final class ActivityThread extends ClientTransactionHandler { boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, - String buildSerial, boolean autofillCompatibilityEnabled) { + String buildSerial, boolean autofillCompatibilityEnabled, + long[] disabledCompatChanges) { if (services != null) { if (false) { @@ -968,6 +971,7 @@ public final class ActivityThread extends ClientTransactionHandler { data.initProfilerInfo = profilerInfo; data.buildSerial = buildSerial; data.autofillCompatibilityEnabled = autofillCompatibilityEnabled; + data.disabledCompatChanges = disabledCompatChanges; sendMessage(H.BIND_APPLICATION, data); } @@ -5670,6 +5674,7 @@ public final class ActivityThread extends ClientTransactionHandler { // Note when this process has started. Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); + AppCompatCallbacks.install(data.disabledCompatChanges); mBoundApplication = data; mConfiguration = new Configuration(data.config); mCompatConfiguration = new Configuration(data.config); diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java new file mode 100644 index 000000000000..17697dba9ccd --- /dev/null +++ b/core/java/android/app/AppCompatCallbacks.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.compat.Compatibility; +import android.os.Process; +import android.util.Log; + +import java.util.Arrays; + +/** + * App process implementation of the {@link Compatibility} API. + * + * @hide + */ +public final class AppCompatCallbacks extends Compatibility.Callbacks { + + private static final String TAG = "Compatibility"; + + private final long[] mDisabledChanges; + + /** + * Install this class into the current process. + * + * @param disabledChanges Set of compatibility changes that are disabled for this process. + */ + public static void install(long[] disabledChanges) { + Compatibility.setCallbacks(new AppCompatCallbacks(disabledChanges)); + } + + private AppCompatCallbacks(long[] disabledChanges) { + mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length); + Arrays.sort(mDisabledChanges); + } + + protected void reportChange(long changeId) { + Log.d(TAG, "Compat change reported: " + changeId + "; UID " + Process.myUid()); + // TODO log via StatsLog + } + + protected boolean isChangeEnabled(long changeId) { + if (Arrays.binarySearch(mDisabledChanges, changeId) < 0) { + // Not present in the disabled array + reportChange(changeId); + return true; + } + return false; + } + +} diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index d478cd6e5946..1f45fc5b8752 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -67,7 +67,8 @@ oneway interface IApplicationThread { int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode, boolean persistent, in Configuration config, in CompatibilityInfo compatInfo, in Map services, - in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled); + in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled, + in long[] disabledCompatChanges); void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs); void scheduleExit(); void scheduleServiceArgs(IBinder token, in ParceledListSlice args); diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 556ffa24368f..1c1fad3beb8c 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -1592,5 +1592,15 @@ public class JobInfo implements Parcelable { } return new JobInfo(this); } + + /** + * @hide + */ + public String summarize() { + final String service = (mJobService != null) + ? mJobService.flattenToShortString() + : "null"; + return "JobInfo.Builder{job:" + mJobId + "/" + service + "}"; + } } } diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 79c0a3a207c4..c79df1743446 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -428,6 +428,43 @@ public final class BluetoothCodecConfig implements Parcelable { } /** + * Checks whether a value set presented by a bitmask has zero or single bit + * + * @param valueSet the value set presented by a bitmask + * @return true if the valueSet contains zero or single bit, otherwise false. + */ + private static boolean hasSingleBit(int valueSet) { + return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0); + } + + /** + * Checks whether the object contains none or single sample rate. + * + * @return true if the object contains none or single sample rate, otherwise false. + */ + public boolean hasSingleSampleRate() { + return hasSingleBit(mSampleRate); + } + + /** + * Checks whether the object contains none or single bits per sample. + * + * @return true if the object contains none or single bits per sample, otherwise false. + */ + public boolean hasSingleBitsPerSample() { + return hasSingleBit(mBitsPerSample); + } + + /** + * Checks whether the object contains none or single channel mode. + * + * @return true if the object contains none or single channel mode, otherwise false. + */ + public boolean hasSingleChannelMode() { + return hasSingleBit(mChannelMode); + } + + /** * Checks whether the audio feeding parameters are same. * * @param other the codec config to compare against @@ -438,4 +475,58 @@ public final class BluetoothCodecConfig implements Parcelable { && other.mBitsPerSample == mBitsPerSample && other.mChannelMode == mChannelMode); } + + /** + * Checks whether another codec config has the similar feeding parameters. + * Any parameters with NONE value will be considered to be a wildcard matching. + * + * @param other the codec config to compare against + * @return true if the audio feeding parameters are similar, otherwise false. + */ + public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { + if (other == null || mCodecType != other.mCodecType) { + return false; + } + int sampleRate = other.mSampleRate; + if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE + || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { + sampleRate = mSampleRate; + } + int bitsPerSample = other.mBitsPerSample; + if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE + || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + bitsPerSample = mBitsPerSample; + } + int channelMode = other.mChannelMode; + if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE + || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { + channelMode = mChannelMode; + } + return sameAudioFeedingParameters(new BluetoothCodecConfig( + mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode, + /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0, + /* specific4 */ 0)); + } + + /** + * Checks whether the codec specific parameters are the same. + * + * @param other the codec config to compare against + * @return true if the codec specific parameters are the same, otherwise false. + */ + public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { + if (other == null && mCodecType != other.mCodecType) { + return false; + } + // Currently we only care about the LDAC Playback Quality at CodecSpecific1 + switch (mCodecType) { + case SOURCE_CODEC_TYPE_LDAC: + if (mCodecSpecific1 != other.mCodecSpecific1) { + return false; + } + // fall through + default: + return true; + } + } } diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 32bb681f2e89..8237d6a73c77 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -88,6 +88,43 @@ public final class BluetoothCodecStatus implements Parcelable { return Arrays.asList(c1).containsAll(Arrays.asList(c2)); } + /** + * Checks whether the codec config matches the selectable capabilities. + * Any parameters of the codec config with NONE value will be considered a wildcard matching. + * + * @param codecConfig the codec config to compare against + * @return true if the codec config matches, otherwise false + */ + public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) { + if (codecConfig == null || !codecConfig.hasSingleSampleRate() + || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) { + return false; + } + for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) { + if (codecConfig.getCodecType() != selectableConfig.getCodecType()) { + continue; + } + int sampleRate = codecConfig.getSampleRate(); + if ((sampleRate & selectableConfig.getSampleRate()) == 0 + && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) { + continue; + } + int bitsPerSample = codecConfig.getBitsPerSample(); + if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0 + && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + continue; + } + int channelMode = codecConfig.getChannelMode(); + if ((channelMode & selectableConfig.getChannelMode()) == 0 + && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) { + continue; + } + return true; + } + return false; + } + + @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 05833b5f571d..5d00f09501b0 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -126,6 +126,17 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { "android.bluetooth.headsetclient.profile.action.RESULT"; /** + * Intent that notifies about vendor specific event arrival. Events not defined in + * HFP spec will be matched with supported vendor event list and this intent will + * be broadcasted upon a match. Supported vendor events are of format of + * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx". + * Vendor event can be a response to an vendor specific command or unsolicited. + * + */ + public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT = + "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT"; + + /** * Intent that notifies about the number attached to the last voice tag * recorded on AG. * @@ -243,6 +254,28 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final String EXTRA_CME_CODE = "android.bluetooth.headsetclient.extra.CME_CODE"; + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * indicates vendor ID. + */ + public static final String EXTRA_VENDOR_ID = + "android.bluetooth.headsetclient.extra.VENDOR_ID"; + + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * indicates vendor event code. + */ + public static final String EXTRA_VENDOR_EVENT_CODE = + "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE"; + + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * contains full vendor event including event code and full arguments. + */ + public static final String EXTRA_VENDOR_EVENT_FULL_ARGS = + "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS"; + + /* Extras for AG_FEATURES, extras type is boolean */ // TODO verify if all of those are actually useful /** @@ -588,6 +621,31 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { } /** + * Send vendor specific AT command. + * + * @param device remote device + * @param vendorId vendor number by Bluetooth SIG + * @param atCommand command to be sent. It start with + prefix and only one command at one time. + * @return <code>true</code> if command has been issued successfully; <code>false</code> + * otherwise. + */ + public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, + String atCommand) { + if (DBG) log("sendVendorSpecificCommand()"); + final IBluetoothHeadsetClient service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.sendVendorAtCommand(device, vendorId, atCommand); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** * Stops voice recognition. * * @param device remote device diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 99ff53dcb904..4ab8c5fcf820 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3037,6 +3037,7 @@ public abstract class Context { TELEPHONY_SERVICE, TELEPHONY_SUBSCRIPTION_SERVICE, CARRIER_CONFIG_SERVICE, + EUICC_SERVICE, TELECOM_SERVICE, CLIPBOARD_SERVICE, INPUT_METHOD_SERVICE, @@ -3216,6 +3217,8 @@ public abstract class Context { * @see android.telephony.SubscriptionManager * @see #CARRIER_CONFIG_SERVICE * @see android.telephony.CarrierConfigManager + * @see #EUICC_SERVICE + * @see android.telephony.euicc.EuiccManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager * @see #UI_MODE_SERVICE diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 4f7f07bdee55..f7c9635f67de 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1949,6 +1949,30 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports Open Mobile API capable UICC-based secure + * elements. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports Open Mobile API capable eSE-based secure + * elements. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports Open Mobile API capable SD-based secure + * elements. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports the OpenGL ES * <a href="http://www.khronos.org/registry/gles/extensions/ANDROID/ANDROID_extension_pack_es31a.txt"> * Android Extension Pack</a>. diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index 7d101b8f78e5..888380b41e58 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -32,8 +32,8 @@ import android.view.WindowManager; import android.view.WindowManager.LayoutParams; /** - * CompatibilityInfo class keeps the information about compatibility mode that the application is - * running under. + * CompatibilityInfo class keeps the information about the screen compatibility mode that the + * application is running under. * * {@hide} */ diff --git a/core/java/android/net/LinkQualityInfo.java b/core/java/android/net/LinkQualityInfo.java index b6f88255c0e2..78d77872fc04 100644 --- a/core/java/android/net/LinkQualityInfo.java +++ b/core/java/android/net/LinkQualityInfo.java @@ -24,8 +24,8 @@ import android.os.Parcelable; * Class that represents useful attributes of generic network links * such as the upload/download throughput or packet error rate. * Generally speaking, you should be dealing with instances of - * LinkQualityInfo subclasses, such as {@link android.net.#WifiLinkQualityInfo} - * or {@link android.net.#MobileLinkQualityInfo} which provide additional + * LinkQualityInfo subclasses, such as {@link android.net.WifiLinkQualityInfo} + * or {@link android.net.MobileLinkQualityInfo} which provide additional * information. * @hide */ diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 32735aa24cd5..458348373e0d 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -233,22 +233,5 @@ public final class BugreportManager { Binder.restoreCallingIdentity(identity); } } - - // Old methods; should go away - @Override - public void onProgressUpdated(int progress) throws RemoteException { - // TODO(b/111441001): remove from interface - } - - @Override - public void onMaxProgressUpdated(int maxProgress) throws RemoteException { - // TODO(b/111441001): remove from interface - } - - @Override - public void onSectionComplete(String title, int status, int size, int durationMs) - throws RemoteException { - // TODO(b/111441001): remove from interface - } } } diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index b13e68df3e7c..0c3f29139510 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -123,6 +123,7 @@ public class RemoteCallbackList<E extends IInterface> { IBinder binder = callback.asBinder(); try { Callback cb = new Callback(callback, cookie); + unregister(callback); binder.linkToDeath(cb, 0); mCallbacks.put(binder, cb); return true; diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java index 6025c348853c..2ba39827dab8 100644 --- a/core/java/android/os/SharedMemory.java +++ b/core/java/android/os/SharedMemory.java @@ -25,6 +25,8 @@ import android.system.OsConstants; import dalvik.system.VMRuntime; +import libcore.io.IoUtils; + import java.io.Closeable; import java.io.FileDescriptor; import java.nio.ByteBuffer; @@ -62,7 +64,7 @@ public final class SharedMemory implements Parcelable, Closeable { mMemoryRegistration = new MemoryRegistration(mSize); mCleaner = Cleaner.create(mFileDescriptor, - new Closer(mFileDescriptor, mMemoryRegistration)); + new Closer(mFileDescriptor.getInt$(), mMemoryRegistration)); } /** @@ -259,6 +261,9 @@ public final class SharedMemory implements Parcelable, Closeable { mCleaner.clean(); mCleaner = null; } + + // Cleaner.clean doesn't clear the value of the file descriptor. + mFileDescriptor.setInt$(-1); } @Override @@ -290,19 +295,24 @@ public final class SharedMemory implements Parcelable, Closeable { * Cleaner that closes the FD */ private static final class Closer implements Runnable { + // This is a copy of the FileDescriptor we're attached to, in order to avoid a reference + // cycle. private FileDescriptor mFd; private MemoryRegistration mMemoryReference; - private Closer(FileDescriptor fd, MemoryRegistration memoryReference) { - mFd = fd; + private Closer(int fd, MemoryRegistration memoryReference) { + mFd = new FileDescriptor(); + mFd.setInt$(fd); + IoUtils.setFdOwner(mFd, this); + mMemoryReference = memoryReference; } @Override public void run() { - try { - Os.close(mFd); - } catch (ErrnoException e) { /* swallow error */ } + IoUtils.closeQuietly(mFd); + mFd = null; + mMemoryReference.release(); mMemoryReference = null; } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index e8f46417dd7d..d438103a22b5 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -648,6 +648,31 @@ public class ZygoteProcess { } /** + * Notify the Zygote processes that boot completed. + */ + public void bootCompleted() { + // Notify both the 32-bit and 64-bit zygote. + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + bootCompleted(Build.SUPPORTED_32_BIT_ABIS[0]); + } + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + bootCompleted(Build.SUPPORTED_64_BIT_ABIS[0]); + } + } + + private void bootCompleted(String abi) { + try { + synchronized (mLock) { + ZygoteState state = openZygoteSocketIfNeeded(abi); + state.mZygoteOutputWriter.write("1\n--boot-completed\n"); + state.mZygoteOutputWriter.flush(); + } + } catch (Exception ex) { + throw new RuntimeException("Failed to inform zygote of boot_completed", ex); + } + } + + /** * Push hidden API blacklisting exemptions into the zygote process(es). * * <p>The list of exemptions will take affect for all new processes forked from the zygote after diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index b6ed22c293f6..6f7d456f807a 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -905,10 +905,12 @@ public class NumberPicker extends LinearLayout { if (!mFlingScroller.isFinished()) { mFlingScroller.forceFinished(true); mAdjustScroller.forceFinished(true); + onScrollerFinished(mFlingScroller); onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); } else if (!mAdjustScroller.isFinished()) { mFlingScroller.forceFinished(true); mAdjustScroller.forceFinished(true); + onScrollerFinished(mAdjustScroller); } else if (mLastDownEventY < mTopSelectionDividerTop) { postChangeCurrentByOneFromLongPress( false, ViewConfiguration.getLongPressTimeout()); diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java index fde01dd0deea..750d698db333 100644 --- a/core/java/com/android/internal/app/AssistUtils.java +++ b/core/java/com/android/internal/app/AssistUtils.java @@ -195,7 +195,7 @@ public class AssistUtils { return applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp(); } - private static boolean isDisclosureEnabled(Context context) { + public static boolean isDisclosureEnabled(Context context) { return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ASSIST_DISCLOSURE_ENABLED, 0) != 0; } diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java index df8c6d021466..b3b0ba198651 100644 --- a/core/java/com/android/internal/os/KernelWakelockReader.java +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -27,6 +27,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import java.io.File; import java.io.FileInputStream; import java.util.Iterator; @@ -38,6 +39,7 @@ public class KernelWakelockReader { private static int sKernelWakelockUpdateVersion = 0; private static final String sWakelockFile = "/proc/wakelocks"; private static final String sWakeupSourceFile = "/d/wakeup_sources"; + private static final String sSysClassWakeupDir = "/sys/class/wakeup"; private static final int[] PROC_WAKELOCKS_FORMAT = new int[] { Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name @@ -71,99 +73,125 @@ public class KernelWakelockReader { * @return the updated data. */ public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) { - byte[] buffer = new byte[32*1024]; - int len = 0; - boolean wakeup_sources; - final long startTime = SystemClock.uptimeMillis(); + boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists(); - final int oldMask = StrictMode.allowThreadDiskReadsMask(); - try { - FileInputStream is; + if (useSystemSuspend) { + // Get both kernel and native wakelock stats from SystemSuspend + updateVersion(staleStats); + if (getWakelockStatsFromSystemSuspend(staleStats) == null) { + Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend"); + return null; + } + return removeOldStats(staleStats); + } else { + byte[] buffer = new byte[32*1024]; + int len = 0; + boolean wakeup_sources; + final long startTime = SystemClock.uptimeMillis(); + + final int oldMask = StrictMode.allowThreadDiskReadsMask(); try { - is = new FileInputStream(sWakelockFile); - wakeup_sources = false; - } catch (java.io.FileNotFoundException e) { + FileInputStream is; try { - is = new FileInputStream(sWakeupSourceFile); - wakeup_sources = true; - } catch (java.io.FileNotFoundException e2) { - Slog.wtf(TAG, "neither " + sWakelockFile + " nor " + - sWakeupSourceFile + " exists"); - return null; + is = new FileInputStream(sWakelockFile); + wakeup_sources = false; + } catch (java.io.FileNotFoundException e) { + try { + is = new FileInputStream(sWakeupSourceFile); + wakeup_sources = true; + } catch (java.io.FileNotFoundException e2) { + Slog.wtf(TAG, "neither " + sWakelockFile + " nor " + + sWakeupSourceFile + " exists"); + return null; + } } - } - int cnt; - while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) { - len += cnt; - } - - is.close(); - } catch (java.io.IOException e) { - Slog.wtf(TAG, "failed to read kernel wakelocks", e); - return null; - } finally { - StrictMode.setThreadPolicyMask(oldMask); - } + int cnt; + while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) { + len += cnt; + } - final long readTime = SystemClock.uptimeMillis() - startTime; - if (readTime > 100) { - Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); - } + is.close(); + } catch (java.io.IOException e) { + Slog.wtf(TAG, "failed to read kernel wakelocks", e); + return null; + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } - if (len > 0) { - if (len >= buffer.length) { - Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); + final long readTime = SystemClock.uptimeMillis() - startTime; + if (readTime > 100) { + Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); } - int i; - for (i=0; i<len; i++) { - if (buffer[i] == '\0') { - len = i; - break; + + if (len > 0) { + if (len >= buffer.length) { + Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); + } + int i; + for (i=0; i<len; i++) { + if (buffer[i] == '\0') { + len = i; + break; + } } } - } - - updateVersion(staleStats); - parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + updateVersion(staleStats); + // Get native wakelock stats from SystemSuspend + if (getWakelockStatsFromSystemSuspend(staleStats) == null) { + Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend"); + } + // Get kernel wakelock stats + parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + return removeOldStats(staleStats); + } + } + /** + * On success, returns the updated stats from SystemSupend, else returns null. + */ + private KernelWakelockStats getWakelockStatsFromSystemSuspend( + final KernelWakelockStats staleStats) { + WakeLockInfo[] wlStats = null; if (mSuspendControlService == null) { try { mSuspendControlService = ISuspendControlService.Stub.asInterface( ServiceManager.getServiceOrThrow("suspend_control")); } catch (ServiceNotFoundException e) { Slog.wtf(TAG, "Required service suspend_control not available", e); + return null; } } try { - WakeLockInfo[] wlStats = mSuspendControlService.getWakeLockStats(); - getNativeWakelockStats(wlStats, staleStats); + wlStats = mSuspendControlService.getWakeLockStats(); + updateWakelockStats(wlStats, staleStats); } catch (RemoteException e) { Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e); + return null; } - return removeOldStats(staleStats); + return staleStats; } /** - * Reads native wakelock stats from SystemSuspend and updates staleStats with the new - * information. + * Updates statleStats with stats from SystemSuspend. * @param staleStats Existing object to update. * @return the updated stats. */ @VisibleForTesting - public KernelWakelockStats getNativeWakelockStats(WakeLockInfo[] wlStats, + public KernelWakelockStats updateWakelockStats(WakeLockInfo[] wlStats, final KernelWakelockStats staleStats) { for (WakeLockInfo info : wlStats) { if (!staleStats.containsKey(info.name)) { staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount, - info.totalTime, sKernelWakelockUpdateVersion)); + info.totalTime * 1000 /* ms to us */, sKernelWakelockUpdateVersion)); } else { KernelWakelockStats.Entry kwlStats = staleStats.get(info.name); kwlStats.mCount = (int) info.activeCount; - kwlStats.mTotalTime = info.totalTime; + // Convert milliseconds to microseconds + kwlStats.mTotalTime = info.totalTime * 1000; kwlStats.mVersion = sKernelWakelockUpdateVersion; } } diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index af90b150b4c6..c64103f4baf9 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -169,6 +169,11 @@ class ZygoteArguments { boolean mPidQuery; /** + * Whether the current arguments constitute a notification that boot completed. + */ + boolean mBootCompleted; + + /** * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, or * when they change, via --set-api-blacklist-exemptions. */ @@ -330,6 +335,8 @@ class ZygoteArguments { mAbiListQuery = true; } else if (arg.equals("--get-pid")) { mPidQuery = true; + } else if (arg.equals("--boot-completed")) { + mBootCompleted = true; } else if (arg.startsWith("--instruction-set=")) { mInstructionSet = arg.substring(arg.indexOf('=') + 1); } else if (arg.startsWith("--app-data-dir=")) { @@ -364,7 +371,11 @@ class ZygoteArguments { } } - if (mAbiListQuery || mPidQuery) { + if (mBootCompleted) { + if (args.length - curArg > 0) { + throw new IllegalArgumentException("Unexpected arguments after --boot-completed"); + } + } else if (mAbiListQuery || mPidQuery) { if (args.length - curArg > 0) { throw new IllegalArgumentException("Unexpected arguments after --query-abi-list."); } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index ad9f64cd2b0b..fc25a47b0edd 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -149,6 +149,11 @@ class ZygoteConnection { parsedArgs = new ZygoteArguments(args); + if (parsedArgs.mBootCompleted) { + handleBootCompleted(); + return null; + } + if (parsedArgs.mAbiListQuery) { handleAbiListQuery(); return null; @@ -291,6 +296,10 @@ class ZygoteConnection { } } + private void handleBootCompleted() { + VMRuntime.bootCompleted(); + } + /** * Preloads resources if the zygote is in lazily preload mode. Writes the result of the * preload operation; {@code 0} when a preload was initiated due to this request and {@code 1} diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 62c4d76ed455..bbd8ffe686a8 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -306,6 +306,8 @@ static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss) whichHeap = HEAP_NATIVE; } else if (strncmp(name, "[stack", 6) == 0) { whichHeap = HEAP_STACK; + } else if (strncmp(name, "[anon:stack_and_tls:", 20) == 0) { + whichHeap = HEAP_STACK; } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) { whichHeap = HEAP_SO; is_swappable = true; diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 614a8ff124ea..04b06095b022 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -308,7 +308,8 @@ static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerHandl buffer->getHeight(), buffer->getPixelFormat(), (jint)buffer->getUsage(), - (jlong)buffer.get()); + (jlong)buffer.get(), + false /* capturedSecureLayers */); } static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) { diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 480b1eaaf98c..d31df38c626a 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -12,7 +12,7 @@ yaochen@google.com yro@google.com # Settings UI -per-file settings_enums.proto=zhfan@google.com +per-file settings_enums.proto=tmfang@google.com # Frameworks ogunwale@google.com @@ -20,3 +20,6 @@ jjaggi@google.com # Launcher hyunyoungs@google.com + +# Graphics stats +jreck@google.com diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1fce5be4def0..72462ad44386 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1172,6 +1172,9 @@ <bool name="config_use_strict_phone_number_comparation">false</bool> + <!-- The character count of the minimum match for comparison phone numbers --> + <integer name="config_phonenumber_compare_min_match">7</integer> + <!-- Display low battery warning when battery level dips to this value. Also, the battery stats are flushed to disk when we hit this level. --> <integer name="config_criticalBatteryWarningLevel">5</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e8b2b1f3cecd..9f8baf8e5594 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -290,6 +290,7 @@ <java-symbol type="bool" name="config_ui_enableFadingMarquee" /> <java-symbol type="bool" name="config_enableHapticTextHandle" /> <java-symbol type="bool" name="config_use_strict_phone_number_comparation" /> + <java-symbol type="integer" name="config_phonenumber_compare_min_match" /> <java-symbol type="bool" name="config_single_volume" /> <java-symbol type="bool" name="config_voice_capable" /> <java-symbol type="bool" name="config_requireCallCapableAccountForHandle" /> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index a90948780d9e..2fa44876e425 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -55,6 +55,8 @@ android_test { resource_dirs: ["res"], resource_zips: [":FrameworksCoreTests_apks_as_resources"], + + data: [":BstatsTestApp"], } // Rules to copy all the test apks to the intermediate raw resource directory diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/core/tests/coretests/BstatsTestApp/Android.bp index 424c71a4d8d0..a89d72830686 100644 --- a/core/tests/coretests/BstatsTestApp/Android.bp +++ b/core/tests/coretests/BstatsTestApp/Android.bp @@ -15,10 +15,6 @@ android_test_helper_app { name: "BstatsTestApp", - test_suites: [ - "device-tests", - ], - static_libs: ["coretests-aidl"], srcs: ["**/*.java"], diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index d922c16a9297..b9f5ef996fc5 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -412,7 +412,8 @@ public class TransactionParcelTests { IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1, - boolean autofillCompatEnabled) throws RemoteException { + boolean autofillCompatEnabled, long[] disableCompatChanges) + throws RemoteException { } @Override diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java index 008085e38dbf..dc9208de7198 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java @@ -68,12 +68,7 @@ public class KernelWakelockReaderTest extends TestCase { private WakeLockInfo createWakeLockInfo(String name, int activeCount, long totalTime) { WakeLockInfo info = new WakeLockInfo(); info.name = name; - info.pid = 1; info.activeCount = activeCount; - info.isActive = true; - info.activeSince = 0; - info.lastChange = 0; - info.maxTime = 0; info.totalTime = totalTime; return info; } @@ -89,7 +84,7 @@ public class KernelWakelockReaderTest extends TestCase { byte[] buffer, WakeLockInfo[] wlStats) { mReader.updateVersion(staleStats); mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats); - mReader.getNativeWakelockStats(wlStats, staleStats); + mReader.updateWakelockStats(wlStats, staleStats); return mReader.removeOldStats(staleStats); } @@ -101,7 +96,7 @@ public class KernelWakelockReaderTest extends TestCase { mReader = new KernelWakelockReader(); } -// ------------------------- Kernel Wakelock Stats Test ------------------------ +// ------------------------- Legacy Wakelock Stats Test ------------------------ @SmallTest public void testParseEmptyFile() throws Exception { KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true, @@ -196,10 +191,10 @@ public class KernelWakelockReaderTest extends TestCase { assertFalse(staleStats.containsKey("Fakelock")); } -// -------------------- Native (SystemSuspend) Wakelock Stats Test ------------------- +// -------------------- SystemSuspend Wakelock Stats Test ------------------- @SmallTest public void testEmptyWakeLockInfoList() { - KernelWakelockStats staleStats = mReader.getNativeWakelockStats(new WakeLockInfo[0], + KernelWakelockStats staleStats = mReader.updateWakelockStats(new WakeLockInfo[0], new KernelWakelockStats()); assertTrue(staleStats.isEmpty()); @@ -208,9 +203,9 @@ public class KernelWakelockReaderTest extends TestCase { @SmallTest public void testOneWakeLockInfo() { WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock", 20, 10000); + wlStats[0] = createWakeLockInfo("WakeLock", 20, 1000); // Milliseconds - KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats, + KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats, new KernelWakelockStats()); assertEquals(1, staleStats.size()); @@ -219,16 +214,16 @@ public class KernelWakelockReaderTest extends TestCase { KernelWakelockStats.Entry entry = staleStats.get("WakeLock"); assertEquals(20, entry.mCount); - assertEquals(10000, entry.mTotalTime); + assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds } @SmallTest public void testTwoWakeLockInfos() { WakeLockInfo[] wlStats = new WakeLockInfo[2]; - wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); - wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000); + wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds + wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds - KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats, + KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats, new KernelWakelockStats()); assertEquals(2, staleStats.size()); @@ -238,17 +233,17 @@ public class KernelWakelockReaderTest extends TestCase { KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1"); assertEquals(10, entry1.mCount); - assertEquals(1000, entry1.mTotalTime); + assertEquals(1000 * 1000, entry1.mTotalTime); // Microseconds KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); assertEquals(20, entry2.mCount); - assertEquals(2000, entry2.mTotalTime); + assertEquals(2000 * 1000, entry2.mTotalTime); // Microseconds } @SmallTest public void testWakeLockInfosBecomeStale() { WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); + wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds KernelWakelockStats staleStats = new KernelWakelockStats(); @@ -259,9 +254,9 @@ public class KernelWakelockReaderTest extends TestCase { assertTrue(staleStats.containsKey("WakeLock1")); KernelWakelockStats.Entry entry = staleStats.get("WakeLock1"); assertEquals(10, entry.mCount); - assertEquals(1000, entry.mTotalTime); + assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds - wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000); + wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds readKernelWakelockStats(staleStats, new byte[0], wlStats); @@ -271,7 +266,7 @@ public class KernelWakelockReaderTest extends TestCase { assertTrue(staleStats.containsKey("WakeLock2")); entry = staleStats.get("WakeLock2"); assertEquals(20, entry.mCount); - assertEquals(2000, entry.mTotalTime); + assertEquals(2000 * 1000, entry.mTotalTime); // Micro seconds } // -------------------- Aggregate Wakelock Stats Tests -------------------- @@ -313,7 +308,7 @@ public class KernelWakelockReaderTest extends TestCase { byte[] buffer = new byte[0]; WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000); + wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000); // Milliseconds readKernelWakelockStats(staleStats, buffer, wlStats); @@ -323,7 +318,7 @@ public class KernelWakelockReaderTest extends TestCase { KernelWakelockStats.Entry entry = staleStats.get("WakeLock"); assertEquals(10, entry.mCount); - assertEquals(1000, entry.mTotalTime); + assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds } @SmallTest @@ -334,7 +329,7 @@ public class KernelWakelockReaderTest extends TestCase { .addLine("WakeLock1", 34, 123) // Milliseconds .getBytes(); WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000); + wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000); // Milliseconds readKernelWakelockStats(staleStats, buffer, wlStats); @@ -348,7 +343,7 @@ public class KernelWakelockReaderTest extends TestCase { assertTrue(staleStats.containsKey("WakeLock2")); KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); assertEquals(10, entry2.mCount); - assertEquals(1000, entry2.mTotalTime); + assertEquals(1000 * 1000, entry2.mTotalTime); // Microseconds } @SmallTest @@ -360,8 +355,8 @@ public class KernelWakelockReaderTest extends TestCase { .addLine("WakeLock2", 46, 345) // Milliseconds .getBytes(); WakeLockInfo[] wlStats = new WakeLockInfo[2]; - wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000); - wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000); + wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000); // Milliseconds + wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000); // Milliseconds readKernelWakelockStats(staleStats, buffer, wlStats); @@ -382,18 +377,18 @@ public class KernelWakelockReaderTest extends TestCase { KernelWakelockStats.Entry entry3 = staleStats.get("WakeLock3"); assertEquals(10, entry3.mCount); - assertEquals(1000, entry3.mTotalTime); + assertEquals(1000 * 1000, entry3.mTotalTime); // Microseconds KernelWakelockStats.Entry entry4 = staleStats.get("WakeLock4"); assertEquals(20, entry4.mCount); - assertEquals(2000, entry4.mTotalTime); + assertEquals(2000 * 1000, entry4.mTotalTime); // Microseconds buffer = new ProcFileBuilder() .addLine("WakeLock1", 45, 789) // Milliseconds .addLine("WakeLock1", 56, 123) // Milliseconds .getBytes(); wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000); + wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000); // Milliseconds readKernelWakelockStats(staleStats, buffer, wlStats); @@ -411,6 +406,6 @@ public class KernelWakelockReaderTest extends TestCase { entry2 = staleStats.get("WakeLock4"); assertEquals(40, entry2.mCount); - assertEquals(4000, entry4.mTotalTime); + assertEquals(4000 * 1000, entry4.mTotalTime); // Microseconds } } diff --git a/media/OWNERS b/media/OWNERS index 72c89529974b..0a12adcf2ea5 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -9,6 +9,7 @@ insun@google.com jaewan@google.com jmtrivi@google.com jsharkey@android.com +klhyun@google.com lajos@google.com marcone@google.com sungsoo@google.com diff --git a/media/java/android/media/AudioPortConfig.java b/media/java/android/media/AudioPortConfig.java index 45e49a7fb3e1..ac19bb167905 100644 --- a/media/java/android/media/AudioPortConfig.java +++ b/media/java/android/media/AudioPortConfig.java @@ -95,7 +95,6 @@ public class AudioPortConfig { /** * The gain configuration if this port supports gain control, null otherwise - * @see AudioGainConfig. */ public AudioGainConfig gain() { return mGain; diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java index f9a4b1e90ad2..6d9d6265f5e7 100644 --- a/media/java/android/media/AudioPortEventHandler.java +++ b/media/java/android/media/AudioPortEventHandler.java @@ -19,10 +19,12 @@ package android.media; import android.annotation.UnsupportedAppUsage; import android.os.Handler; import android.os.HandlerThread; -import android.os.Looper; import android.os.Message; -import java.util.ArrayList; + +import com.android.internal.annotations.GuardedBy; + import java.lang.ref.WeakReference; +import java.util.ArrayList; /** * The AudioPortEventHandler handles AudioManager.OnAudioPortUpdateListener callbacks @@ -33,6 +35,9 @@ import java.lang.ref.WeakReference; class AudioPortEventHandler { private Handler mHandler; private HandlerThread mHandlerThread; + private final Object mLock = new Object(); + + @GuardedBy("mLock") private final ArrayList<AudioManager.OnAudioPortUpdateListener> mListeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); @@ -53,7 +58,7 @@ class AudioPortEventHandler { private long mJniCallback; void init() { - synchronized (this) { + synchronized (mLock) { if (mHandler != null) { return; } @@ -66,7 +71,7 @@ class AudioPortEventHandler { @Override public void handleMessage(Message msg) { ArrayList<AudioManager.OnAudioPortUpdateListener> listeners; - synchronized (this) { + synchronized (mLock) { if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) { listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); if (mListeners.contains(msg.obj)) { @@ -152,7 +157,7 @@ class AudioPortEventHandler { private native void native_finalize(); void registerListener(AudioManager.OnAudioPortUpdateListener l) { - synchronized (this) { + synchronized (mLock) { mListeners.add(l); } if (mHandler != null) { @@ -162,7 +167,7 @@ class AudioPortEventHandler { } void unregisterListener(AudioManager.OnAudioPortUpdateListener l) { - synchronized (this) { + synchronized (mLock) { mListeners.remove(l); } } diff --git a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt index d06e5ec634de..f10a3ac42b77 100644 --- a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt +++ b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt @@ -63,7 +63,7 @@ class BrushPropertyDrawable : Drawable { } override fun draw(c: Canvas) { - c?.let { + c.let { val w = bounds.width().toFloat() val h = bounds.height().toFloat() val inset = _size / 12 // 2dp in a 24x24 icon diff --git a/packages/InputDevices/Android.bp b/packages/InputDevices/Android.bp new file mode 100644 index 000000000000..7532aea23615 --- /dev/null +++ b/packages/InputDevices/Android.bp @@ -0,0 +1,42 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_app { + name: "InputDevices", + + srcs: [ + "**/*.java", + ":validate_input_devices_keymaps", + ], + + resource_dirs: ["res"], + + sdk_version: "current", + certificate: "platform", + privileged: true, +} + +// Validate all key maps. +// Produces an empty srcjar that is used as an input to InputDevices to make sure +// the check runs for platform builds. +genrule { + name: "validate_input_devices_keymaps", + tools: [ + "validatekeymaps", + "soong_zip", + ], + srcs: ["res/raw/*.kcm"], + out: ["validate_input_devices_keymaps.srcjar"], + cmd: "$(location validatekeymaps) -q $(in) && $(location soong_zip) -o $(out)", +} diff --git a/packages/InputDevices/Android.mk b/packages/InputDevices/Android.mk deleted file mode 100644 index 6de1f1d43f72..000000000000 --- a/packages/InputDevices/Android.mk +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2012 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_JAVA_LIBRARIES := - -LOCAL_PACKAGE_NAME := InputDevices -LOCAL_SDK_VERSION := current -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) - -# Validate all key maps. -include $(CLEAR_VARS) - -LOCAL_MODULE := validate_input_devices_keymaps -intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON) -LOCAL_BUILT_MODULE := $(intermediates)/stamp - -validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX) -input_devices_keymaps := $(wildcard $(LOCAL_PATH)/res/raw/*.kcm) -$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps) -$(LOCAL_BUILT_MODULE) : $(input_devices_keymaps) | $(validatekeymaps) - $(hide) $(PRIVATE_VALIDATEKEYMAPS) $^ - $(hide) mkdir -p $(dir $@) && touch $@ - -# Run validatekeymaps unconditionally for platform build. -droidcore : $(LOCAL_BUILT_MODULE) - -# Reset temp vars. -validatekeymaps := -input_devices_keymaps := diff --git a/packages/MtpDocumentsProvider/Android.bp b/packages/MtpDocumentsProvider/Android.bp new file mode 100644 index 000000000000..3dafa2649af6 --- /dev/null +++ b/packages/MtpDocumentsProvider/Android.bp @@ -0,0 +1,11 @@ +android_app { + name: "MtpDocumentsProvider", + + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "media", + privileged: true, + optimize: { + proguard_flags_files: ["proguard.flags"], + }, +} diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk deleted file mode 100644 index 2d62a07566bf..000000000000 --- a/packages/MtpDocumentsProvider/Android.mk +++ /dev/null @@ -1,18 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_PACKAGE_NAME := MtpDocumentsProvider -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := media -LOCAL_PRIVILEGED_MODULE := true -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -# Only enable asserts on userdebug/eng builds -ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT))) -LOCAL_JACK_FLAGS += -D jack.assert.policy=always -endif - -include $(BUILD_PACKAGE) -include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 842879764771..53ccd1bbe681 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -112,7 +112,6 @@ <uses-permission android:name="android.permission.BIND_APPWIDGET" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/> - <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" /> <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 1060c7b7ce79..3750a8a1ae75 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -175,9 +175,9 @@ public class BugreportProgressService extends Service { // Passed to Message.obtain() when msg.arg2 is not used. private static final int UNUSED_ARG2 = -2; - // Maximum progress displayed (like 99.00%). - private static final int CAPPED_PROGRESS = 9900; - private static final int CAPPED_MAX = 10000; + // Maximum progress displayed in %. + private static final int CAPPED_PROGRESS = 99; + private static final int CAPPED_MAX = 100; /** Show the progress log every this percent. */ private static final int LOG_PROGRESS_STEP = 10; @@ -1954,7 +1954,10 @@ public class BugreportProgressService extends Service { @Override public void onProgress(int progress) throws RemoteException { - updateProgressInfo(progress, 100 /* progress is already a percentage; so max = 100 */); + if (progress > CAPPED_PROGRESS) { + progress = CAPPED_PROGRESS; + } + updateProgressInfo(progress, CAPPED_MAX); } @Override @@ -1967,46 +1970,6 @@ public class BugreportProgressService extends Service { // TODO(b/111441001): implement } - @Override - public void onProgressUpdated(int progress) throws RemoteException { - /* - * Checks whether the progress changed in a way that should be displayed to the user: - * - info.progress / info.max represents the displayed progress - * - info.realProgress / info.realMax represents the real progress - * - since the real progress can decrease, the displayed progress is only updated if it - * increases - * - the displayed progress is capped at a maximum (like 99%) - */ - info.realProgress = progress; - final int oldPercentage = (CAPPED_MAX * info.progress) / info.max; - int newPercentage = (CAPPED_MAX * info.realProgress) / info.realMax; - int max = info.realMax; - - if (newPercentage > CAPPED_PROGRESS) { - progress = newPercentage = CAPPED_PROGRESS; - max = CAPPED_MAX; - } - - if (newPercentage > oldPercentage) { - updateProgressInfo(progress, max); - } - } - - @Override - public void onMaxProgressUpdated(int maxProgress) throws RemoteException { - Log.d(TAG, "onMaxProgressUpdated: " + maxProgress); - info.realMax = maxProgress; - } - - @Override - public void onSectionComplete(String title, int status, int size, int durationMs) - throws RemoteException { - if (DEBUG) { - Log.v(TAG, "Title: " + title + " Status: " + status + " Size: " + size - + " Duration: " + durationMs + "ms"); - } - } - public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("token: "); pw.println(token); } diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp new file mode 100644 index 000000000000..d91a1162034b --- /dev/null +++ b/packages/SimAppDialog/Android.bp @@ -0,0 +1,15 @@ +android_app { + name: "SimAppDialog", + + srcs: ["src/**/*.java"], + + platform_apis: true, + certificate: "platform", + + static_libs: [ + "android-support-v4", + "setup-wizard-lib", + ], + + resource_dirs: ["res"], +} diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk deleted file mode 100644 index 6a4099bcc637..000000000000 --- a/packages/SimAppDialog/Android.mk +++ /dev/null @@ -1,19 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := SimAppDialog -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - - -LOCAL_STATIC_ANDROID_LIBRARIES := \ - android-support-v4 - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -include frameworks/opt/setupwizard/library/common-platform-deprecated.mk - -include $(BUILD_PACKAGE) diff --git a/packages/SystemUI/plugin/ExamplePlugin/Android.bp b/packages/SystemUI/plugin/ExamplePlugin/Android.bp index a0eaf14f4a06..c6c80f3780a3 100644 --- a/packages/SystemUI/plugin/ExamplePlugin/Android.bp +++ b/packages/SystemUI/plugin/ExamplePlugin/Android.bp @@ -11,4 +11,5 @@ android_app { srcs: ["src/**/*.java"], + platform_apis: true, } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 03fb9bbdbd4e..0fcb99445a44 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -340,7 +340,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe case SimPuk: // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled( + if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser())) { finish = true; } else { diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 38a90cfd96a5..c906240f781c 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -219,7 +219,7 @@ public class AssistManager implements ConfigurationChangedReceiver { intent.setComponent(assistComponent); intent.putExtras(args); - if (structureEnabled) { + if (structureEnabled && AssistUtils.isDisclosureEnabled(mContext)) { showDisclosure(); } diff --git a/services/core/Android.bp b/services/core/Android.bp index 5ddb68701814..45584aa96016 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -12,12 +12,14 @@ java_library_static { }, srcs: [ "java/**/*.java", + ":platformcompat_aidl", ":dumpstate_aidl", ":installd_aidl", ":storaged_aidl", ":vold_aidl", ":gsiservice_aidl", ":mediaupdateservice_aidl", + ":platform-compat-config", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/policy/EventLogTags.logtags", @@ -78,3 +80,11 @@ prebuilt_etc { name: "gps_debug.conf", src: "java/com/android/server/location/gps_debug.conf", } + +filegroup { + name: "platformcompat_aidl", + srcs: [ + "java/com/android/server/compat/IPlatformCompat.aidl", + ], + path: "java", +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 223eb552f832..89b59cf4a734 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1142,7 +1142,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isBluetoothDisallowed) { return; } - if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { + final boolean isSafeMode = mContext.getPackageManager().isSafeMode(); + if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) { if (DBG) { Slog.d(TAG, "Auto-enabling Bluetooth."); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 231c01588e4b..667445b1c58f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -149,7 +149,6 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.Xml; @@ -167,7 +166,6 @@ import com.android.internal.util.AsyncChannel; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; -import com.android.internal.util.WakeupMessage; import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.AutodestructReference; @@ -304,7 +302,8 @@ public class ConnectivityService extends IConnectivityManager.Stub /** Flag indicating if background data is restricted. */ private boolean mRestrictBackground; - final private Context mContext; + private final Context mContext; + private final Dependencies mDeps; // 0 is full bad, 100 is full good private int mDefaultInetConditionPublished = 0; @@ -585,11 +584,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private NetworkNotificationManager mNotifier; private LingerMonitor mLingerMonitor; - // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp - private static final int MIN_NET_ID = 100; // some reserved marks - private static final int MAX_NET_ID = 65535 - 0x0400; // Top 1024 bits reserved by IpSecService - private int mNextNetId = MIN_NET_ID; - // sequence number of NetworkRequests private int mNextNetworkRequestId = 1; @@ -833,19 +827,113 @@ public class ConnectivityService extends IConnectivityManager.Stub } }; + /** + * Dependencies of ConnectivityService, for injection in tests. + */ + @VisibleForTesting + public static class Dependencies { + /** + * Get system properties to use in ConnectivityService. + */ + public MockableSystemProperties getSystemProperties() { + return new MockableSystemProperties(); + } + + /** + * Create a HandlerThread to use in ConnectivityService. + */ + public HandlerThread makeHandlerThread() { + return new HandlerThread("ConnectivityServiceThread"); + } + + /** + * Get a reference to the NetworkStackClient. + */ + public NetworkStackClient getNetworkStack() { + return NetworkStackClient.getInstance(); + } + + /** + * @see Tethering + */ + public Tethering makeTethering(@NonNull Context context, + @NonNull INetworkManagementService nms, + @NonNull INetworkStatsService statsService, + @NonNull INetworkPolicyManager policyManager, + @NonNull TetheringDependencies tetheringDeps) { + return new Tethering(context, nms, statsService, policyManager, + IoThread.get().getLooper(), getSystemProperties(), tetheringDeps); + } + + /** + * @see ProxyTracker + */ + public ProxyTracker makeProxyTracker(@NonNull Context context, + @NonNull Handler connServiceHandler) { + return new ProxyTracker(context, connServiceHandler, EVENT_PROXY_HAS_CHANGED); + } + + /** + * @see NetIdManager + */ + public NetIdManager makeNetIdManager() { + return new NetIdManager(); + } + + /** + * @see NetworkUtils#queryUserAccess(int, int) + */ + public boolean queryUserAccess(int uid, int netId) { + return NetworkUtils.queryUserAccess(uid, netId); + } + + /** + * @see MultinetworkPolicyTracker + */ + public MultinetworkPolicyTracker makeMultinetworkPolicyTracker( + @NonNull Context c, @NonNull Handler h, @NonNull Runnable r) { + return new MultinetworkPolicyTracker(c, h, r); + } + + /** + * @see ServiceManager#checkService(String) + */ + public boolean hasService(@NonNull String name) { + return ServiceManager.checkService(name) != null; + } + + /** + * @see IpConnectivityMetrics.Logger + */ + public IpConnectivityMetrics.Logger getMetricsLogger() { + return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class), + "no IpConnectivityMetrics service"); + } + + /** + * @see IpConnectivityMetrics + */ + public IIpConnectivityMetrics getIpConnectivityMetrics() { + return IIpConnectivityMetrics.Stub.asInterface( + ServiceManager.getService(IpConnectivityLog.SERVICE_NAME)); + } + } + public ConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager) { - this(context, netManager, statsService, policyManager, - getDnsResolver(), new IpConnectivityLog(), NetdService.getInstance()); + this(context, netManager, statsService, policyManager, getDnsResolver(), + new IpConnectivityLog(), NetdService.getInstance(), new Dependencies()); } @VisibleForTesting protected ConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager, - IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd) { + IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) { if (DBG) log("ConnectivityService starting up"); - mSystemProperties = getSystemProperties(); + mDeps = checkNotNull(deps, "missing Dependencies"); + mSystemProperties = mDeps.getSystemProperties(); + mNetIdManager = mDeps.makeNetIdManager(); mMetricsLog = logger; mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST); @@ -862,7 +950,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultWifiRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST); - mHandlerThread = new HandlerThread("ConnectivityServiceThread"); + mHandlerThread = mDeps.makeHandlerThread(); mHandlerThread.start(); mHandler = new InternalHandler(mHandlerThread.getLooper()); mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper()); @@ -880,7 +968,7 @@ public class ConnectivityService extends IConnectivityManager.Stub LocalServices.getService(NetworkPolicyManagerInternal.class), "missing NetworkPolicyManagerInternal"); mDnsResolver = checkNotNull(dnsresolver, "missing IDnsResolver"); - mProxyTracker = makeProxyTracker(); + mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler); mNetd = netd; mKeyStore = KeyStore.getInstance(); @@ -948,7 +1036,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Do the same for Ethernet, since it's often not specified in the configs, although many // devices can use it via USB host adapters. - if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) { + if (mNetConfigs[TYPE_ETHERNET] == null && mDeps.hasService(Context.ETHERNET_SERVICE)) { mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET); mNetworksDefined++; } @@ -968,7 +1056,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - mTethering = makeTethering(); + mTethering = deps.makeTethering(mContext, mNMS, mStatsService, mPolicyManager, + makeTetheringDependencies()); mPermissionMonitor = new PermissionMonitor(mContext, mNetd); @@ -1027,7 +1116,7 @@ public class ConnectivityService extends IConnectivityManager.Stub LingerMonitor.DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS); mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit); - mMultinetworkPolicyTracker = createMultinetworkPolicyTracker( + mMultinetworkPolicyTracker = mDeps.makeMultinetworkPolicyTracker( mContext, mHandler, () -> rematchForAvoidBadWifiUpdate()); mMultinetworkPolicyTracker.start(); @@ -1037,10 +1126,8 @@ public class ConnectivityService extends IConnectivityManager.Stub registerPrivateDnsSettingsCallbacks(); } - @VisibleForTesting - protected Tethering makeTethering() { - // TODO: Move other elements into @Overridden getters. - final TetheringDependencies deps = new TetheringDependencies() { + private TetheringDependencies makeTetheringDependencies() { + return new TetheringDependencies() { @Override public boolean isTetheringSupported() { return ConnectivityService.this.isTetheringSupported(); @@ -1050,14 +1137,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return mDefaultRequest; } }; - return new Tethering(mContext, mNMS, mStatsService, mPolicyManager, - IoThread.get().getLooper(), new MockableSystemProperties(), - deps); - } - - @VisibleForTesting - protected ProxyTracker makeProxyTracker() { - return new ProxyTracker(mContext, mHandler, EVENT_PROXY_HAS_CHANGED); } private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { @@ -1149,22 +1228,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return mNextNetworkRequestId++; } - @VisibleForTesting - protected int reserveNetId() { - synchronized (mNetworkForNetId) { - for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) { - int netId = mNextNetId; - if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID; - // Make sure NetID unused. http://b/16815182 - if (!mNetIdInUse.get(netId)) { - mNetIdInUse.put(netId, true); - return netId; - } - } - } - throw new IllegalStateException("No free netIds"); - } - private NetworkState getFilteredNetworkState(int networkType, int uid) { if (mLegacyTypeTracker.isTypeSupported(networkType)) { final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); @@ -1796,11 +1859,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } }; - @VisibleForTesting - protected void registerNetdEventCallback() { - final IIpConnectivityMetrics ipConnectivityMetrics = - IIpConnectivityMetrics.Stub.asInterface( - ServiceManager.getService(IpConnectivityLog.SERVICE_NAME)); + private void registerNetdEventCallback() { + final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics(); if (ipConnectivityMetrics == null) { Slog.wtf(TAG, "Missing IIpConnectivityMetrics"); return; @@ -2236,12 +2296,6 @@ public class ConnectivityService extends IConnectivityManager.Stub protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208"; private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd"; - // Overridden for testing purposes to avoid writing to SystemProperties. - @VisibleForTesting - protected MockableSystemProperties getSystemProperties() { - return new MockableSystemProperties(); - } - private void updateTcpBufferSizes(String tcpBufferSizes) { String[] values = null; if (tcpBufferSizes != null) { @@ -2631,8 +2685,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (valid != nai.lastValidated) { if (wasDefault) { - metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity( - SystemClock.elapsedRealtime(), valid); + mDeps.getMetricsLogger() + .defaultNetworkMetrics().logDefaultNetworkValidity( + SystemClock.elapsedRealtime(), valid); } final int oldScore = nai.getCurrentScore(); nai.lastValidated = valid; @@ -2967,8 +3022,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean wasDefault = isDefaultNetwork(nai); synchronized (mNetworkForNetId) { mNetworkForNetId.remove(nai.network.netId); - mNetIdInUse.delete(nai.network.netId); } + mNetIdManager.releaseNetId(nai.network.netId); // Just in case. mLegacyTypeTracker.remove(nai, wasDefault); } @@ -3015,7 +3070,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence // whose timestamps tell how long it takes to recover a default network. long now = SystemClock.elapsedRealtime(); - metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai); + mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai); } notifyIfacesChangedForNetworkStats(); // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied @@ -3069,9 +3124,7 @@ public class ConnectivityService extends IConnectivityManager.Stub destroyNativeNetwork(nai); mDnsManager.removeNetwork(nai.network); } - synchronized (mNetworkForNetId) { - mNetIdInUse.delete(nai.network.netId); - } + mNetIdManager.releaseNetId(nai.network.netId); } private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) { @@ -4155,7 +4208,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return null; } return getLinkPropertiesProxyInfo(activeNetwork); - } else if (queryUserAccess(Binder.getCallingUid(), network.netId)) { + } else if (mDeps.queryUserAccess(Binder.getCallingUid(), network.netId)) { // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which // caller may not have. return getLinkPropertiesProxyInfo(network); @@ -4164,10 +4217,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return null; } - @VisibleForTesting - protected boolean queryUserAccess(int uid, int netId) { - return NetworkUtils.queryUserAccess(uid, netId); - } private ProxyInfo getLinkPropertiesProxyInfo(Network network) { final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); @@ -4760,7 +4809,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final long ident = Binder.clearCallingIdentity(); try { // Concatenate the range of types onto the range of NetIDs. - int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE); + int id = NetIdManager.MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE); mNotifier.setProvNotificationVisible(visible, id, action); } finally { Binder.restoreCallingIdentity(ident); @@ -5371,10 +5420,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @GuardedBy("mNetworkForNetId") private final SparseArray<NetworkAgentInfo> mNetworkForNetId = new SparseArray<>(); // NOTE: Accessed on multiple threads, synchronized with mNetworkForNetId. - // An entry is first added to mNetIdInUse, prior to mNetworkForNetId, so + // An entry is first reserved with NetIdManager, prior to being added to mNetworkForNetId, so // there may not be a strict 1:1 correlation between the two. - @GuardedBy("mNetworkForNetId") - private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray(); + private final NetIdManager mNetIdManager; // NetworkAgentInfo keyed off its connecting messenger // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays @@ -5476,9 +5524,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // satisfies mDefaultRequest. final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), - new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore, - mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mDnsResolver, - mNMS, factorySerialNumber); + new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, + currentScore, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, + mDnsResolver, mNMS, factorySerialNumber); // Make sure the network capabilities reflect what the agent info says. nai.setNetworkCapabilities(mixInCapabilities(nai, nc)); final String extraInfo = networkInfo.getExtraInfo(); @@ -5487,7 +5535,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("registerNetworkAgent " + nai); final long token = Binder.clearCallingIdentity(); try { - getNetworkStack().makeNetworkMonitor( + mDeps.getNetworkStack().makeNetworkMonitor( nai.network, name, new NetworkMonitorCallbacks(nai)); } finally { Binder.restoreCallingIdentity(token); @@ -5499,11 +5547,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return nai.network.netId; } - @VisibleForTesting - protected NetworkStackClient getNetworkStack() { - return NetworkStackClient.getInstance(); - } - private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) { nai.onNetworkMonitorCreated(networkMonitor); if (VDBG) log("Got NetworkAgent Messenger"); @@ -5519,7 +5562,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger); NetworkInfo networkInfo = nai.networkInfo; - nai.networkInfo = null; updateNetworkInfo(nai, networkInfo); updateUids(nai, null, nai.networkCapabilities); } @@ -6313,7 +6355,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Notify system services that this network is up. makeDefault(newNetwork); // Log 0 -> X and Y -> X default network transitions, where X is the new default. - metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent( + mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent( now, newNetwork, oldDefaultNetwork); // Have a new default network, release the transition wakelock in scheduleReleaseNetworkTransitionWakelock(); @@ -6518,8 +6560,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) { log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " + - (oldInfo == null ? "null" : oldInfo.getState()) + - " to " + state); + oldInfo.getState() + " to " + state); } if (!networkAgent.created @@ -6592,8 +6633,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // TODO(b/122649188): send the broadcast only to VPN users. mProxyTracker.sendProxyBroadcast(); } - } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) || - state == NetworkInfo.State.SUSPENDED) { + } else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED || + state == NetworkInfo.State.SUSPENDED)) { // going into or coming out of SUSPEND: re-score and notify if (networkAgent.getCurrentScore() != oldScore) { rematchAllNetworksAndRequests(networkAgent, oldScore); @@ -6985,27 +7026,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return nwm.getWatchlistConfigHash(); } - @VisibleForTesting - MultinetworkPolicyTracker createMultinetworkPolicyTracker(Context c, Handler h, Runnable r) { - return new MultinetworkPolicyTracker(c, h, r); - } - - @VisibleForTesting - public WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int cmd, Object obj) { - return new WakeupMessage(c, h, s, cmd, 0, 0, obj); - } - - @VisibleForTesting - public boolean hasService(String name) { - return ServiceManager.checkService(name) != null; - } - - @VisibleForTesting - protected IpConnectivityMetrics.Logger metricsLogger() { - return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class), - "no IpConnectivityMetrics service"); - } - private void logNetworkEvent(NetworkAgentInfo nai, int evtype) { int[] transports = nai.networkCapabilities.getTransportTypes(); mMetricsLog.log(nai.network.netId, transports, new NetworkEvent(evtype)); diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java index 4639d7586f83..70569db5e2d3 100644 --- a/services/core/java/com/android/server/GraphicsStatsService.java +++ b/services/core/java/com/android/server/GraphicsStatsService.java @@ -191,7 +191,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub { if (!file.getFileDescriptor().valid()) { throw new IllegalStateException("Invalid file descriptor"); } - return new ParcelFileDescriptor(file.getFileDescriptor()); + return ParcelFileDescriptor.dup(file.getFileDescriptor()); } catch (IOException ex) { throw new IllegalStateException("Failed to get PFD from memory file", ex); } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index b4e1c32f2535..a0946a0d2262 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -750,10 +750,10 @@ public class IpSecService extends IIpSecService.Stub { } } - // These values have been reserved in ConnectivityService + // These values have been reserved in NetIdManager @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00; - @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400; + public static final int TUN_INTF_NETID_RANGE = 0x0400; private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray(); private int mNextTunnelNetIdIndex = 0; diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java new file mode 100644 index 000000000000..11533beade56 --- /dev/null +++ b/services/core/java/com/android/server/NetIdManager.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.annotation.NonNull; +import android.util.SparseBooleanArray; + +import com.android.internal.annotations.GuardedBy; + +/** + * Class used to reserve and release net IDs. + * + * <p>Instances of this class are thread-safe. + */ +public class NetIdManager { + // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp + public static final int MIN_NET_ID = 100; // some reserved marks + // Top IDs reserved by IpSecService + public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE; + + @GuardedBy("mNetIdInUse") + private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray(); + + @GuardedBy("mNetIdInUse") + private int mLastNetId = MIN_NET_ID - 1; + + /** + * Get the first netId that follows the provided lastId and is available. + */ + private static int getNextAvailableNetIdLocked( + int lastId, @NonNull SparseBooleanArray netIdInUse) { + int netId = lastId; + for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) { + netId = netId < MAX_NET_ID ? netId + 1 : MIN_NET_ID; + if (!netIdInUse.get(netId)) { + return netId; + } + } + throw new IllegalStateException("No free netIds"); + } + + /** + * Reserve a new ID for a network. + */ + public int reserveNetId() { + synchronized (mNetIdInUse) { + mLastNetId = getNextAvailableNetIdLocked(mLastNetId, mNetIdInUse); + // Make sure NetID unused. http://b/16815182 + mNetIdInUse.put(mLastNetId, true); + return mLastNetId; + } + } + + /** + * Clear a previously reserved ID for a network. + */ + public void releaseNetId(int id) { + synchronized (mNetIdInUse) { + mNetIdInUse.delete(id); + } + } +} diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 371e5177ede3..d46758c3dc39 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1452,10 +1452,11 @@ class StorageManagerService extends IStorageManager.Stub } private void start() { - connect(); + connectStoraged(); + connectVold(); } - private void connect() { + private void connectStoraged() { IBinder binder = ServiceManager.getService("storaged"); if (binder != null) { try { @@ -1464,7 +1465,7 @@ class StorageManagerService extends IStorageManager.Stub public void binderDied() { Slog.w(TAG, "storaged died; reconnecting"); mStoraged = null; - connect(); + connectStoraged(); } }, 0); } catch (RemoteException e) { @@ -1478,7 +1479,17 @@ class StorageManagerService extends IStorageManager.Stub Slog.w(TAG, "storaged not found; trying again"); } - binder = ServiceManager.getService("vold"); + if (mStoraged == null) { + BackgroundThread.getHandler().postDelayed(() -> { + connectStoraged(); + }, DateUtils.SECOND_IN_MILLIS); + } else { + onDaemonConnected(); + } + } + + private void connectVold() { + IBinder binder = ServiceManager.getService("vold"); if (binder != null) { try { binder.linkToDeath(new DeathRecipient() { @@ -1486,7 +1497,7 @@ class StorageManagerService extends IStorageManager.Stub public void binderDied() { Slog.w(TAG, "vold died; reconnecting"); mVold = null; - connect(); + connectVold(); } }, 0); } catch (RemoteException e) { @@ -1506,9 +1517,9 @@ class StorageManagerService extends IStorageManager.Stub Slog.w(TAG, "vold not found; trying again"); } - if (mStoraged == null || mVold == null) { + if (mVold == null) { BackgroundThread.getHandler().postDelayed(() -> { - connect(); + connectVold(); }, DateUtils.SECOND_IN_MILLIS); } else { onDaemonConnected(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4eafe7c3eca0..78b2914b6653 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -452,20 +452,10 @@ import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.ThreadPriorityBooster; import com.android.server.Watchdog; -import com.android.server.am.ActivityManagerServiceDumpActivitiesProto; -import com.android.server.am.ActivityManagerServiceDumpBroadcastsProto; -import com.android.server.am.ActivityManagerServiceDumpProcessesProto; import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; -import com.android.server.am.ActivityManagerServiceDumpServicesProto; import com.android.server.am.ActivityStack.ActivityState; -import com.android.server.am.GrantUriProto; -import com.android.server.am.ImportanceTokenProto; -import com.android.server.am.MemInfoDumpProto; import com.android.server.am.MemoryStatUtil.MemoryStat; -import com.android.server.am.NeededUriGrantsProto; -import com.android.server.am.ProcessOomProto; -import com.android.server.am.ProcessToGcProto; -import com.android.server.am.StickyBroadcastProto; +import com.android.server.compat.CompatConfig; import com.android.server.firewall.IntentFirewall; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; @@ -478,12 +468,12 @@ import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; - import libcore.io.IoUtils; import libcore.util.EmptyArray; +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -7673,6 +7663,7 @@ public class ActivityManagerService extends IActivityManager.Stub checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); bindApplicationTimeMillis = SystemClock.elapsedRealtime(); mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app); + long[] disabledCompatChanges = CompatConfig.get().getDisabledChanges(app.info); if (app.isolatedEntryPoint != null) { // This is an isolated process which should just call an entry point instead of // being bound to an application. @@ -7688,7 +7679,7 @@ public class ActivityManagerService extends IActivityManager.Stub new Configuration(getGlobalConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial, isAutofillCompatEnabled); + buildSerial, isAutofillCompatEnabled, disabledCompatChanges); } else { thread.bindApplication(processName, appInfo, providers, null, profilerInfo, null, null, null, testMode, @@ -7697,7 +7688,7 @@ public class ActivityManagerService extends IActivityManager.Stub new Configuration(getGlobalConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial, isAutofillCompatEnabled); + buildSerial, isAutofillCompatEnabled, disabledCompatChanges); } if (profilerInfo != null) { profilerInfo.closeFd(); @@ -7907,6 +7898,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } + // Let the ART runtime in zygote and system_server know that the boot completed. + ZYGOTE_PROCESS.bootCompleted(); + VMRuntime.bootCompleted(); + IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); pkgFilter.addDataScheme("package"); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 3399a76f358f..6596cff4b6b4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -16,6 +16,15 @@ package com.android.server.am; +import static android.app.ActivityManager.RESIZE_MODE_SYSTEM; +import static android.app.ActivityManager.RESIZE_MODE_USER; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.Display.INVALID_DISPLAY; + +import static com.android.server.am.TaskRecord.INVALID_TASK_ID; + import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.AppGlobals; @@ -74,6 +83,7 @@ import android.view.Display; import com.android.internal.util.HexDump; import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; +import com.android.server.compat.CompatConfig; import java.io.BufferedReader; import java.io.File; @@ -96,15 +106,6 @@ import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; -import static android.app.ActivityManager.RESIZE_MODE_SYSTEM; -import static android.app.ActivityManager.RESIZE_MODE_USER; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.view.Display.INVALID_DISPLAY; - -import static com.android.server.am.TaskRecord.INVALID_TASK_ID; - final class ActivityManagerShellCommand extends ShellCommand { public static final String NO_CLASS_ERROR_CODE = "Error type 3"; private static final String SHELL_PACKAGE_NAME = "com.android.shell"; @@ -277,6 +278,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runNoHomeScreen(pw); case "wait-for-broadcast-idle": return runWaitForBroadcastIdle(pw); + case "compat": + return runCompat(pw); default: return handleDefaultCommands(cmd); } @@ -2794,6 +2797,50 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + private int runCompat(PrintWriter pw) { + final CompatConfig config = CompatConfig.get(); + String toggleValue = getNextArgRequired(); + long changeId; + String changeIdString = getNextArgRequired(); + try { + changeId = Long.parseLong(changeIdString); + } catch (NumberFormatException e) { + changeId = config.lookupChangeId(changeIdString); + } + if (changeId == -1) { + pw.println("Unknown or invalid change: '" + changeIdString + "'."); + } + String packageName = getNextArgRequired(); + switch(toggleValue) { + case "enable": + if (!config.addOverride(changeId, packageName, true)) { + pw.println("Warning! Change " + changeId + " is not known yet. Enabling it" + + " could have no effect."); + } + pw.println("Enabled change " + changeId + " for " + packageName + "."); + return 0; + case "disable": + if (!config.addOverride(changeId, packageName, false)) { + pw.println("Warning! Change " + changeId + " is not known yet. Disabling it" + + " could have no effect."); + } + pw.println("Disabled change " + changeId + " for " + packageName + "."); + return 0; + case "reset": + if (config.removeOverride(changeId, packageName)) { + pw.println("Reset change " + changeId + " for " + packageName + + " to default value."); + } else { + pw.println("No override exists for changeId " + changeId + "."); + } + return 0; + default: + pw.println("Invalid toggle value: '" + toggleValue + "'."); + } + return -1; + } + + private Resources getResources(PrintWriter pw) throws RemoteException { // system resources does not contain all the device configuration, construct it manually. Configuration config = mInterface.getConfiguration(); @@ -3090,6 +3137,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" without restarting any processes."); pw.println(" write"); pw.println(" Write all pending state to storage."); + pw.println(" compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>"); + pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>."); pw.println(); Intent.printIntentArgsHelp(pw, ""); } diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index bb3b9be2bd2f..6f32beea66d3 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -20,6 +20,8 @@ import android.annotation.Nullable; import android.compat.annotation.EnabledAfter; import android.content.pm.ApplicationInfo; +import com.android.server.compat.config.Change; + import java.util.HashMap; import java.util.Map; @@ -60,6 +62,16 @@ public final class CompatChange { mDisabled = disabled; } + /** + * @param change an object generated by services/core/xsd/platform-compat-config.xsd + */ + public CompatChange(Change change) { + mChangeId = change.getId(); + mName = change.getName(); + mEnableAfterTargetSdk = change.getEnableAfterTargetSdk(); + mDisabled = change.getDisabled(); + } + long getId() { return mChangeId; } @@ -106,6 +118,12 @@ public final class CompatChange { * @return {@code true} if the change should be enabled for the package. */ boolean isEnabled(ApplicationInfo app) { + if (app.isSystemApp()) { + // All changes are enabled for system apps, and we do not support overrides. + // Compatibility issues for system apps should be addressed in the app itself when + // the compatibility change is made. + return true; + } if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) { return mPackageOverrides.get(app.packageName); } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index fea5d836ac25..044e41789bb2 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -17,13 +17,27 @@ package com.android.server.compat; import android.content.pm.ApplicationInfo; +import android.os.Environment; import android.text.TextUtils; import android.util.LongArray; import android.util.LongSparseArray; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.compat.config.Change; +import com.android.server.compat.config.XmlParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + +import javax.xml.datatype.DatatypeConfigurationException; /** * This class maintains state relating to platform compatibility changes. * @@ -32,7 +46,12 @@ import com.android.internal.annotations.VisibleForTesting; */ public final class CompatConfig { - private static final CompatConfig sInstance = new CompatConfig(); + private static final String TAG = "CompatConfig"; + private static final String CONFIG_FILE_SUFFIX = "platform_compat_config.xml"; + + private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib( + Environment.buildPath( + Environment.getRootDirectory(), "etc", "sysconfig")); @GuardedBy("mChanges") private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); @@ -128,20 +147,24 @@ public final class CompatConfig { * <p>Note, package overrides are not persistent and will be lost on system or runtime restart. * * @param changeId The ID of the change to be overridden. Note, this call will succeed even if - * this change is not known; it will only have any affect if any code in the + * this change is not known; it will only have any effect if any code in the * platform is gated on the ID given. * @param packageName The app package name to override the change for. * @param enabled If the change should be enabled or disabled. + * @return {@code true} if the change existed before adding the override. */ - public void addOverride(long changeId, String packageName, boolean enabled) { + public boolean addOverride(long changeId, String packageName, boolean enabled) { + boolean alreadyKnown = true; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); if (c == null) { + alreadyKnown = false; c = new CompatChange(changeId); addChange(c); } c.addPackageOverride(packageName, enabled); } + return alreadyKnown; } /** @@ -151,14 +174,61 @@ public final class CompatConfig { * * @param changeId The ID of the change that was overridden. * @param packageName The app package name that was overridden. + * @return {@code true} if an override existed; */ - public void removeOverride(long changeId, String packageName) { + public boolean removeOverride(long changeId, String packageName) { + boolean overrideExists = false; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); if (c != null) { + overrideExists = true; c.removePackageOverride(packageName); } } + return overrideExists; + } + + /** + * Dumps the current list of compatibility config information. + * + * @param pw The {@link PrintWriter} instance to which the information will be dumped. + */ + public void dumpConfig(PrintWriter pw) { + synchronized (mChanges) { + if (mChanges.size() == 0) { + pw.println("No compat overrides."); + return; + } + for (int i = 0; i < mChanges.size(); ++i) { + CompatChange c = mChanges.valueAt(i); + pw.println(c.toString()); + } + } + } + + CompatConfig initConfigFromLib(File libraryDir) { + if (!libraryDir.exists() || !libraryDir.isDirectory()) { + Slog.e(TAG, "No directory " + libraryDir + ", skipping"); + return this; + } + for (File f : libraryDir.listFiles()) { + //TODO(b/138222363): Handle duplicate ids across config files. + if (f.getPath().endsWith(CONFIG_FILE_SUFFIX)) { + readConfig(f); + } + } + return this; + } + + private void readConfig(File configFile) { + try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { + for (Change change : XmlParser.read(in).getCompatChange()) { + Slog.w(TAG, "Adding: " + change.toString()); + addChange(new CompatChange(change)); + } + } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { + Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e); + } } } diff --git a/services/core/java/com/android/server/compat/IPlatformCompat.aidl b/services/core/java/com/android/server/compat/IPlatformCompat.aidl new file mode 100644 index 000000000000..8ab08f9047cb --- /dev/null +++ b/services/core/java/com/android/server/compat/IPlatformCompat.aidl @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.compat; + +import android.content.pm.ApplicationInfo; + +/** + * System private API for talking with the PlatformCompat service. + * {@hide} + */ +interface IPlatformCompat +{ + + /** + * Reports that a compatibility change is affecting an app process now. + * + * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)}, + * you do not need to call this API directly. The change will be reported for you in the case + * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}. + * + * @param changeId The ID of the compatibility change taking effect. + * @param appInfo Representing the affected app. + */ + void reportChange(long changeId, in ApplicationInfo appInfo); + + /** + * Query if a given compatibility change is enabled for an app process. This method should + * be called when implementing functionality on behalf of the affected app. + * + * <p>If this method returns {@code true}, the calling code should implement the compatibility + * change, resulting in differing behaviour compared to earlier releases. If this method returns + * {@code false}, the calling code should behave as it did in earlier releases. + * + * <p>When this method returns {@code true}, it will also report the change as + * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method + * directly. + * + * @param changeId The ID of the compatibility change in question. + * @param appInfo Representing the app in question. + * @return {@code true} if the change is enabled for the current app. + */ + boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo); +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 456d15e4fba8..3eea194fd73e 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -16,52 +16,46 @@ package com.android.server.compat; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.util.Slog; +import com.android.internal.util.DumpUtils; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * System server internal API for gating and reporting compatibility changes. */ -public class PlatformCompat { +public class PlatformCompat extends IPlatformCompat.Stub { private static final String TAG = "Compatibility"; - /** - * Reports that a compatibility change is affecting an app process now. - * - * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)}, - * you do not need to call this API directly. The change will be reported for you in the case - * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}. - * - * @param changeId The ID of the compatibility change taking effect. - * @param appInfo Representing the affected app. - */ - public static void reportChange(long changeId, ApplicationInfo appInfo) { + private final Context mContext; + + public PlatformCompat(Context context) { + mContext = context; + } + + @Override + public void reportChange(long changeId, ApplicationInfo appInfo) { Slog.d(TAG, "Compat change reported: " + changeId + "; UID " + appInfo.uid); // TODO log via StatsLog } - /** - * Query if a given compatibility change is enabled for an app process. This method should - * be called when implementing functionality on behalf of the affected app. - * - * <p>If this method returns {@code true}, the calling code should implement the compatibility - * change, resulting in differing behaviour compared to earlier releases. If this method returns - * {@code false}, the calling code should behave as it did in earlier releases. - * - * <p>When this method returns {@code true}, it will also report the change as - * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method - * directly. - * - * @param changeId The ID of the compatibility change in question. - * @param appInfo Representing the app in question. - * @return {@code true} if the change is enabled for the current app. - */ - public static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { + @Override + public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) { reportChange(changeId, appInfo); return true; } return false; } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; + CompatConfig.get().dumpConfig(pw); + } } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 5b043799f848..96b7cb315f58 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -16,6 +16,7 @@ package com.android.server.connectivity; +import android.annotation.NonNull; import android.content.Context; import android.net.IDnsResolver; import android.net.INetd; @@ -116,7 +117,7 @@ import java.util.TreeSet; // not, ConnectivityService disconnects the NetworkAgent's AsyncChannel. public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { - public NetworkInfo networkInfo; + @NonNull public NetworkInfo networkInfo; // This Network object should always be used if possible, so as to encourage reuse of the // enclosed socket factory and connection pool. Avoid creating other Network objects. // This Network object is always valid. @@ -579,10 +580,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } if (newExpiry > 0) { - mLingerMessage = mConnService.makeWakeupMessage( + mLingerMessage = new WakeupMessage( mContext, mHandler, - "NETWORK_LINGER_COMPLETE." + network.netId, - EVENT_NETWORK_LINGER_COMPLETE, this); + "NETWORK_LINGER_COMPLETE." + network.netId /* cmdName */, + EVENT_NETWORK_LINGER_COMPLETE /* cmd */, + 0 /* arg1 (unused) */, 0 /* arg2 (unused) */, + this /* obj (NetworkAgentInfo) */); mLingerMessage.schedule(newExpiry); } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 4957eed21c70..73d160d8c444 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -772,7 +772,6 @@ public class Tethering extends BaseNetworkObserver { case WifiManager.WIFI_AP_STATE_FAILED: default: disableWifiIpServingLocked(ifname, curState); - mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_WIFI); break; } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java index adc1cd7fc1d3..8148216ba176 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java @@ -290,8 +290,7 @@ final class HdmiCecKeycode { new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP), new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN), new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL), - // No Android keycode defined for CEC_KEYCODE_SOUND_SELECT - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SOUND_SELECT), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK, CEC_KEYCODE_SOUND_SELECT), new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT), new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION), // No Android keycode defined for CEC_KEYCODE_HELP diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index 4f8b1dcc6bb4..dad435bf6209 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -492,6 +492,9 @@ public final class JobStore { if (jobStatus.hasBatteryNotLowConstraint()) { out.attribute(null, "battery-not-low", Boolean.toString(true)); } + if (jobStatus.hasStorageNotLowConstraint()) { + out.attribute(null, "storage-not-low", Boolean.toString(true)); + } out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS); } @@ -852,6 +855,15 @@ public final class JobStore { jobBuilder.setExtras(extras); parser.nextTag(); // Consume </extras> + final JobInfo builtJob; + try { + builtJob = jobBuilder.build(); + } catch (Exception e) { + Slog.w(TAG, "Unable to build job from XML, ignoring: " + + jobBuilder.summarize()); + return null; + } + // Migrate sync jobs forward from earlier, incomplete representation if ("android".equals(sourcePackageName) && extras != null @@ -935,6 +947,14 @@ public final class JobStore { if (val != null) { jobBuilder.setRequiresCharging(true); } + val = parser.getAttributeValue(null, "battery-not-low"); + if (val != null) { + jobBuilder.setRequiresBatteryNotLow(true); + } + val = parser.getAttributeValue(null, "storage-not-low"); + if (val != null) { + jobBuilder.setRequiresStorageNotLow(true); + } } /** diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS index 4bc937347e04..b460cb5b23ea 100644 --- a/services/core/java/com/android/server/media/OWNERS +++ b/services/core/java/com/android/server/media/OWNERS @@ -2,5 +2,6 @@ elaurent@google.com hdmoon@google.com insun@google.com jaewan@google.com +klhyun@google.com lajos@google.com sungsoo@google.com diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index c98a79ad4ed9..714bbb97c90d 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -297,19 +297,5 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } mDs.asBinder().unlinkToDeath(this, 0); } - - // Old methods; unused in the API flow. - @Override - public void onProgressUpdated(int progress) throws RemoteException { - } - - @Override - public void onMaxProgressUpdated(int maxProgress) throws RemoteException { - } - - @Override - public void onSectionComplete(String title, int status, int size, int durationMs) - throws RemoteException { - } } } diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 2b33aced7151..c7124314cae0 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -281,20 +281,7 @@ public class BackgroundDexOptService extends JobService { mAbortIdleOptimization.set(false); long lowStorageThreshold = getLowStorageThreshold(context); - // Optimize primary apks. - int result = optimizePackages(pm, pkgs, lowStorageThreshold, - /*isForPrimaryDex=*/ true); - if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { - return result; - } - if (supportSecondaryDex()) { - result = reconcileSecondaryDexFiles(pm.getDexManager()); - if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { - return result; - } - result = optimizePackages(pm, pkgs, lowStorageThreshold, - /*isForPrimaryDex=*/ false); - } + int result = idleOptimizePackages(pm, pkgs, lowStorageThreshold); return result; } @@ -342,45 +329,87 @@ public class BackgroundDexOptService extends JobService { return 0; } - private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs, - long lowStorageThreshold, boolean isForPrimaryDex) { + private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs, + long lowStorageThreshold) { ArraySet<String> updatedPackages = new ArraySet<>(); - Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); - boolean hadSomeLowSpaceFailure = false; - Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); - // Only downgrade apps when space is low on device. - // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean - // up disk before user hits the actual lowStorageThreshold. - final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE * - lowStorageThreshold; - boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); - Log.d(TAG, "Should Downgrade " + shouldDowngrade); - boolean dex_opt_performed = false; - for (String pkg : pkgs) { - int abort_code = abortIdleOptimizations(lowStorageThreshold); - if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { - return abort_code; + + try { + final boolean supportSecondaryDex = supportSecondaryDex(); + + if (supportSecondaryDex) { + int result = reconcileSecondaryDexFiles(pm.getDexManager()); + if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { + return result; + } } - // Downgrade unused packages. - if (unusedPackages.contains(pkg) && shouldDowngrade) { - dex_opt_performed = downgradePackage(pm, pkg, isForPrimaryDex); - } else { - if (abort_code == OPTIMIZE_ABORT_NO_SPACE_LEFT) { - // can't dexopt because of low space. - hadSomeLowSpaceFailure = true; - continue; + + // Only downgrade apps when space is low on device. + // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean + // up disk before user hits the actual lowStorageThreshold. + final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE + * lowStorageThreshold; + boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); + Log.d(TAG, "Should Downgrade " + shouldDowngrade); + if (shouldDowngrade) { + Set<String> unusedPackages = + pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); + Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); + + if (!unusedPackages.isEmpty()) { + for (String pkg : unusedPackages) { + int abortCode = abortIdleOptimizations(/*lowStorageThreshold*/ -1); + if (abortCode != OPTIMIZE_CONTINUE) { + // Should be aborted by the scheduler. + return abortCode; + } + if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) { + updatedPackages.add(pkg); + } + if (supportSecondaryDex) { + downgradePackage(pm, pkg, /*isForPrimaryDex*/ false); + } + } + + pkgs = new ArraySet<>(pkgs); + pkgs.removeAll(unusedPackages); } - dex_opt_performed = optimizePackage(pm, pkg, isForPrimaryDex); } - if (dex_opt_performed) { - updatedPackages.add(pkg); + + int primaryResult = optimizePackages(pm, pkgs, lowStorageThreshold, + /*isForPrimaryDex*/ true, updatedPackages); + if (primaryResult != OPTIMIZE_PROCESSED) { + return primaryResult; } - } - notifyPinService(updatedPackages); - return hadSomeLowSpaceFailure ? OPTIMIZE_ABORT_NO_SPACE_LEFT : OPTIMIZE_PROCESSED; + if (!supportSecondaryDex) { + return OPTIMIZE_PROCESSED; + } + + int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold, + /*isForPrimaryDex*/ false, updatedPackages); + return secondaryResult; + } finally { + // Always let the pinner service know about changes. + notifyPinService(updatedPackages); + } } + private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs, + long lowStorageThreshold, boolean isForPrimaryDex, ArraySet<String> updatedPackages) { + for (String pkg : pkgs) { + int abortCode = abortIdleOptimizations(lowStorageThreshold); + if (abortCode != OPTIMIZE_CONTINUE) { + // Either aborted by the scheduler or no space left. + return abortCode; + } + + boolean dexOptPerformed = optimizePackage(pm, pkg, isForPrimaryDex); + if (dexOptPerformed) { + updatedPackages.add(pkg); + } + } + return OPTIMIZE_PROCESSED; + } /** * Try to downgrade the package to a smaller compilation filter. diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index f08e58579975..6ea274d8a814 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -403,6 +403,10 @@ class TvInputHardwareManager implements TvInputHal.Callback { || checkUidChangedLocked(connection, callingUid, resolvedUserId)) { return; } + ITvInputHardwareCallback callback = connection.getCallbackLocked(); + if (callback != null) { + callback.asBinder().unlinkToDeath(connection, 0); + } connection.resetLocked(null, null, null, null, null); } } diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp index 98e4343e6e57..8b2cbbde2db8 100644 --- a/services/core/xsd/Android.bp +++ b/services/core/xsd/Android.bp @@ -4,3 +4,11 @@ xsd_config { api_dir: "schema", package_name: "com.android.server.pm.permission.configfile", } + + +xsd_config { + name: "platform-compat-config", + srcs: ["platform-compat-config.xsd"], + api_dir: "platform-compat-schema", + package_name: "com.android.server.compat.config", +} diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd new file mode 100644 index 000000000000..ee39e507aff1 --- /dev/null +++ b/services/core/xsd/platform-compat-config.xsd @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- This defines the format of the XML file generated by + ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from + ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java. +--> +<xs:schema version="2.0" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:complexType name="change"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute type="xs:long" name="id" use="required"/> + <xs:attribute type="xs:string" name="name" use="required"/> + <xs:attribute type="xs:boolean" name="disabled"/> + <xs:attribute type="xs:int" name="enableAfterTargetSdk"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:element name="config"> + <xs:complexType> + <xs:sequence> + <xs:element name="compat-change" type="change" maxOccurs="unbounded" + minOccurs="0"/> + </xs:sequence> + </xs:complexType> + <xs:unique name="UniqueId"> + <xs:selector xpath="compat-change" /> + <xs:field xpath="@id" /> + </xs:unique> + </xs:element> +</xs:schema> + + + + diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt new file mode 100644 index 000000000000..84567851da2c --- /dev/null +++ b/services/core/xsd/platform-compat-schema/current.txt @@ -0,0 +1,31 @@ +// Signature format: 2.0 +package com.android.server.compat.config { + + public class Change { + ctor public Change(); + method public boolean getDisabled(); + method public int getEnableAfterTargetSdk(); + method public long getId(); + method public String getName(); + method public String getValue(); + method public void setDisabled(boolean); + method public void setEnableAfterTargetSdk(int); + method public void setId(long); + method public void setName(String); + method public void setValue(String); + } + + public class Config { + ctor public Config(); + method public java.util.List<com.android.server.compat.config.Change> getCompatChange(); + } + + public class XmlParser { + ctor public XmlParser(); + method public static com.android.server.compat.config.Config read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + } + +} + diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat-schema/last_current.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/platform-compat-schema/last_current.txt diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat-schema/last_removed.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/platform-compat-schema/last_removed.txt diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat-schema/removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/services/core/xsd/platform-compat-schema/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index dab160338a76..10db049a3e63 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -79,6 +79,7 @@ import com.android.server.audio.AudioService; import com.android.server.broadcastradio.BroadcastRadioService; import com.android.server.camera.CameraServiceProxy; import com.android.server.clipboard.ClipboardService; +import com.android.server.compat.PlatformCompat; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.coverage.CoverageService; import com.android.server.devicepolicy.DevicePolicyManagerService; @@ -975,6 +976,11 @@ public final class SystemServer { traceBeginAndSlog("PinnerService"); mSystemServiceManager.startService(PinnerService.class); traceEnd(); + + traceBeginAndSlog("PlatformCompat"); + ServiceManager.addService("platform_compat", new PlatformCompat(context)); + traceEnd(); + } catch (RuntimeException e) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting core service", e); diff --git a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java index bb56876c77f5..7c41377985d3 100644 --- a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java +++ b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java @@ -21,13 +21,11 @@ package android.net.dhcp; * @hide */ public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub { - // TODO: add @Override here once the API is versioned - /** * Get the version of the aidl interface implemented by the callbacks. */ + @Override public int getInterfaceVersion() { - // TODO: return IDhcpServerCallbacks.VERSION; - return 0; + return IDhcpServerCallbacks.VERSION; } } diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 153820d4853e..41f46f5ef2de 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -38,6 +38,7 @@ android_test { "ub-uiautomator", "platformprotosnano", "servicestests-utils", + "xml-writer-device-lib", ], aidl: { @@ -78,6 +79,8 @@ android_test { optimize: { enabled: false, }, + + data: [":JobTestApp"], } java_library { diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index e6c484a8dbbc..f3c5e99f5f90 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -22,9 +22,17 @@ import android.content.pm.ApplicationInfo; import androidx.test.runner.AndroidJUnit4; +import com.android.compat.annotation.Change; +import com.android.compat.annotation.XmlWriter; + import org.junit.Test; import org.junit.runner.RunWith; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.UUID; + @RunWith(AndroidJUnit4.class) public class CompatConfigTest { @@ -35,6 +43,27 @@ public class CompatConfigTest { return ai; } + private File createTempDir() { + String base = System.getProperty("java.io.tmpdir"); + File dir = new File(base, UUID.randomUUID().toString()); + assertThat(dir.mkdirs()).isTrue(); + return dir; + } + + private void writeChangesToFile(Change[] changes, File f) { + XmlWriter writer = new XmlWriter(); + for (Change change: changes) { + writer.addChange(change); + } + try { + f.createNewFile(); + writer.write(new FileOutputStream(f)); + } catch (IOException e) { + throw new RuntimeException( + "Encountered an error while writing compat config file", e); + } + } + @Test public void testUnknownChangeEnabled() { CompatConfig pc = new CompatConfig(); @@ -142,4 +171,73 @@ public class CompatConfigTest { CompatConfig pc = new CompatConfig(); assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L); } + + @Test + public void testSystemAppDisabledChangeEnabled() { + CompatConfig pc = new CompatConfig(); + pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true)); // disabled + ApplicationInfo sysApp = makeAppInfo("system.app", 1); + sysApp.flags |= ApplicationInfo.FLAG_SYSTEM; + assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue(); + } + + @Test + public void testSystemAppOverrideIgnored() { + CompatConfig pc = new CompatConfig(); + pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false)); + pc.addOverride(1234L, "system.app", false); + ApplicationInfo sysApp = makeAppInfo("system.app", 1); + sysApp.flags |= ApplicationInfo.FLAG_SYSTEM; + assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue(); + } + + @Test + public void testSystemAppTargetSdkIgnored() { + CompatConfig pc = new CompatConfig(); + pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false)); + ApplicationInfo sysApp = makeAppInfo("system.app", 1); + sysApp.flags |= ApplicationInfo.FLAG_SYSTEM; + assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue(); + } + + @Test + public void testReadConfig() { + Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2), new Change(1235L, + "MY_CHANGE2", true, null), new Change(1236L, "MY_CHANGE3", false, null)}; + + File dir = createTempDir(); + writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml")); + + CompatConfig pc = new CompatConfig(); + pc.initConfigFromLib(dir); + + assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); + assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); + assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse(); + assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue(); + } + + @Test + public void testReadConfigMultipleFiles() { + Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2)}; + Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null), new Change(1236L, + "MY_CHANGE3", false, null)}; + + File dir = createTempDir(); + writeChangesToFile(changes1, + new File(dir.getPath() + "/libcore_platform_compat_config.xml")); + writeChangesToFile(changes2, + new File(dir.getPath() + "/frameworks_platform_compat_config.xml")); + + + CompatConfig pc = new CompatConfig(); + pc.initConfigFromLib(dir); + + assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); + assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); + assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse(); + assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue(); + } } + + diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index 543f51cba41f..6fa5cd296923 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -382,6 +382,82 @@ public class JobStoreTest { .build()); } + @Test + public void testPersistedIdleConstraint() throws Exception { + JobInfo.Builder b = new Builder(8, mComponent) + .setRequiresDeviceIdle(true) + .setPersisted(true); + JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); + + mTaskStoreUnderTest.add(taskStatus); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); + assertEquals("Idle constraint not persisted correctly.", + loaded.getJob().isRequireDeviceIdle(), + taskStatus.getJob().isRequireDeviceIdle()); + } + + @Test + public void testPersistedChargingConstraint() throws Exception { + JobInfo.Builder b = new Builder(8, mComponent) + .setRequiresCharging(true) + .setPersisted(true); + JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); + + mTaskStoreUnderTest.add(taskStatus); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); + assertEquals("Charging constraint not persisted correctly.", + loaded.getJob().isRequireCharging(), + taskStatus.getJob().isRequireCharging()); + } + + @Test + public void testPersistedStorageNotLowConstraint() throws Exception { + JobInfo.Builder b = new Builder(8, mComponent) + .setRequiresStorageNotLow(true) + .setPersisted(true); + JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); + + mTaskStoreUnderTest.add(taskStatus); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); + assertEquals("Storage-not-low constraint not persisted correctly.", + loaded.getJob().isRequireStorageNotLow(), + taskStatus.getJob().isRequireStorageNotLow()); + } + + @Test + public void testPersistedBatteryNotLowConstraint() throws Exception { + JobInfo.Builder b = new Builder(8, mComponent) + .setRequiresBatteryNotLow(true) + .setPersisted(true); + JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); + + mTaskStoreUnderTest.add(taskStatus); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); + assertEquals("Battery-not-low constraint not persisted correctly.", + loaded.getJob().isRequireBatteryNotLow(), + taskStatus.getJob().isRequireBatteryNotLow()); + } + /** * Helper function to kick a {@link JobInfo} through a persistence cycle and * assert that it's unchanged. diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp index ae1eca7ba707..b29e187576c3 100644 --- a/services/tests/servicestests/test-apps/JobTestApp/Android.bp +++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp @@ -17,8 +17,6 @@ android_test_helper_app { sdk_version: "current", - test_suites: ["device-tests"], - srcs: ["**/*.java"], dex_preopt: { diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp index 7a1678ac9a5a..a4906d7b4cd3 100644 --- a/startop/apps/test/Android.bp +++ b/startop/apps/test/Android.bp @@ -23,4 +23,5 @@ android_app { "src/FrameLayoutInflationActivity.java", "src/TextViewInflationActivity.java", ], + platform_apis: true, } diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 92ea872f04fc..4f6524e0528b 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -77,7 +77,6 @@ cc_test_host { name: "view-compiler-tests", defaults: ["viewcompiler_defaults"], srcs: [ - "dex_builder_test.cc", "layout_validation_test.cc", "util_test.cc", ], diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc index 6047e8c74e38..499c42e2888b 100644 --- a/startop/view_compiler/dex_builder.cc +++ b/startop/view_compiler/dex_builder.cc @@ -102,6 +102,18 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) { case Instruction::Op::kCheckCast: out << "kCheckCast"; return out; + case Instruction::Op::kGetStaticField: + out << "kGetStaticField"; + return out; + case Instruction::Op::kSetStaticField: + out << "kSetStaticField"; + return out; + case Instruction::Op::kGetInstanceField: + out << "kGetInstanceField"; + return out; + case Instruction::Op::kSetInstanceField: + out << "kSetInstanceField"; + return out; } } @@ -229,6 +241,23 @@ ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) { return type; } +ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::string& name, + TypeDescriptor type) { + const auto key = std::make_tuple(parent, name); + if (field_decls_by_key_.find(key) != field_decls_by_key_.end()) { + return field_decls_by_key_[key]; + } + + ir::FieldDecl* field = Alloc<ir::FieldDecl>(); + field->parent = GetOrAddType(parent); + field->name = GetOrAddString(name); + field->type = GetOrAddType(type); + field->orig_index = dex_file_->fields_indexes.AllocateIndex(); + dex_file_->fields_map[field->orig_index] = field; + field_decls_by_key_[key] = field; + return field; +} + ir::Proto* Prototype::Encode(DexBuilder* dex) const { auto* proto = dex->Alloc<ir::Proto>(); proto->shorty = dex->GetOrAddString(Shorty()); @@ -360,6 +389,11 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) { return EncodeNew(instruction); case Instruction::Op::kCheckCast: return EncodeCast(instruction); + case Instruction::Op::kGetStaticField: + case Instruction::Op::kSetStaticField: + case Instruction::Op::kGetInstanceField: + case Instruction::Op::kSetInstanceField: + return EncodeFieldOp(instruction); } } @@ -428,7 +462,7 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct // first move all the arguments into contiguous temporary registers. std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>(); - const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id()); + const auto& prototype = dex_->GetPrototypeByMethodId(instruction.index_argument()); CHECK(prototype.has_value()); for (size_t i = 0; i < instruction.args().size(); ++i) { @@ -452,12 +486,12 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct Encode3rc(InvokeToInvokeRange(opcode), instruction.args().size(), - instruction.method_id(), + instruction.index_argument(), RegisterValue(scratch[0])); } else { Encode35c(opcode, instruction.args().size(), - instruction.method_id(), + instruction.index_argument(), arguments[0], arguments[1], arguments[2], @@ -514,6 +548,54 @@ void MethodBuilder::EncodeCast(const Instruction& instruction) { Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value()); } +void MethodBuilder::EncodeFieldOp(const Instruction& instruction) { + const auto& args = instruction.args(); + switch (instruction.opcode()) { + case Instruction::Op::kGetStaticField: { + CHECK(instruction.dest().has_value()); + CHECK(instruction.dest()->is_variable()); + CHECK_EQ(0, instruction.args().size()); + + Encode21c(::art::Instruction::SGET, + RegisterValue(*instruction.dest()), + instruction.index_argument()); + break; + } + case Instruction::Op::kSetStaticField: { + CHECK(!instruction.dest().has_value()); + CHECK_EQ(1, args.size()); + CHECK(args[0].is_variable()); + + Encode21c(::art::Instruction::SPUT, RegisterValue(args[0]), instruction.index_argument()); + break; + } + case Instruction::Op::kGetInstanceField: { + CHECK(instruction.dest().has_value()); + CHECK(instruction.dest()->is_variable()); + CHECK_EQ(1, instruction.args().size()); + + Encode22c(::art::Instruction::IGET, + RegisterValue(*instruction.dest()), + RegisterValue(args[0]), + instruction.index_argument()); + break; + } + case Instruction::Op::kSetInstanceField: { + CHECK(!instruction.dest().has_value()); + CHECK_EQ(2, args.size()); + CHECK(args[0].is_variable()); + CHECK(args[1].is_variable()); + + Encode22c(::art::Instruction::IPUT, + RegisterValue(args[1]), + RegisterValue(args[0]), + instruction.index_argument()); + break; + } + default: { LOG(FATAL) << "Unsupported field operation"; } + } +} + size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h index 541d80077bd3..292d6599c115 100644 --- a/startop/view_compiler/dex_builder.h +++ b/startop/view_compiler/dex_builder.h @@ -153,6 +153,8 @@ class Instruction { kBranchEqz, kBranchNEqz, kCheckCast, + kGetInstanceField, + kGetStaticField, kInvokeDirect, kInvokeInterface, kInvokeStatic, @@ -162,6 +164,8 @@ class Instruction { kNew, kReturn, kReturnObject, + kSetInstanceField, + kSetStaticField }; //////////////////////// @@ -170,12 +174,12 @@ class Instruction { // For instructions with no return value and no arguments. static inline Instruction OpNoArgs(Op opcode) { - return Instruction{opcode, /*method_id*/ 0, /*dest*/ {}}; + return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}}; } // For most instructions, which take some number of arguments and have an optional return value. template <typename... T> static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) { - return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...}; + return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...}; } // A cast instruction. Basically, `(type)val` @@ -186,49 +190,71 @@ class Instruction { // For method calls. template <typename... T> - static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { return Instruction{ - Op::kInvokeVirtual, method_id, /*result_is_object=*/false, dest, this_arg, args...}; + Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; } // Returns an object template <typename... T> - static inline Instruction InvokeVirtualObject(size_t method_id, std::optional<const Value> dest, - Value this_arg, T... args) { + static inline Instruction InvokeVirtualObject(size_t index_argument, + std::optional<const Value> dest, Value this_arg, + T... args) { return Instruction{ - Op::kInvokeVirtual, method_id, /*result_is_object=*/true, dest, this_arg, args...}; + Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; } // For direct calls (basically, constructors). template <typename... T> - static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { return Instruction{ - Op::kInvokeDirect, method_id, /*result_is_object=*/false, dest, this_arg, args...}; + Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; } // Returns an object template <typename... T> - static inline Instruction InvokeDirectObject(size_t method_id, std::optional<const Value> dest, - Value this_arg, T... args) { + static inline Instruction InvokeDirectObject(size_t index_argument, + std::optional<const Value> dest, Value this_arg, + T... args) { return Instruction{ - Op::kInvokeDirect, method_id, /*result_is_object=*/true, dest, this_arg, args...}; + Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; } // For static calls. template <typename... T> - static inline Instruction InvokeStatic(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest, T... args) { - return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/false, dest, args...}; + return Instruction{ + Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...}; } // Returns an object template <typename... T> - static inline Instruction InvokeStaticObject(size_t method_id, std::optional<const Value> dest, - T... args) { - return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/true, dest, args...}; + static inline Instruction InvokeStaticObject(size_t index_argument, + std::optional<const Value> dest, T... args) { + return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...}; } // For static calls. template <typename... T> - static inline Instruction InvokeInterface(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest, T... args) { - return Instruction{Op::kInvokeInterface, method_id, /*result_is_object=*/false, dest, args...}; + return Instruction{ + Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...}; + } + + static inline Instruction GetStaticField(size_t field_id, Value dest) { + return Instruction{Op::kGetStaticField, field_id, dest}; + } + + static inline Instruction SetStaticField(size_t field_id, Value value) { + return Instruction{ + Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value}; + } + + static inline Instruction GetField(size_t field_id, Value dest, Value object) { + return Instruction{Op::kGetInstanceField, field_id, /*result_is_object=*/false, dest, object}; + } + + static inline Instruction SetField(size_t field_id, Value object, Value value) { + return Instruction{ + Op::kSetInstanceField, field_id, /*result_is_object=*/false, /*dest=*/{}, object, value}; } /////////////// @@ -236,27 +262,31 @@ class Instruction { /////////////// Op opcode() const { return opcode_; } - size_t method_id() const { return method_id_; } + size_t index_argument() const { return index_argument_; } bool result_is_object() const { return result_is_object_; } const std::optional<const Value>& dest() const { return dest_; } const std::vector<const Value>& args() const { return args_; } private: - inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest) - : opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {} + inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest) + : opcode_{opcode}, + index_argument_{index_argument}, + result_is_object_{false}, + dest_{dest}, + args_{} {} template <typename... T> - inline constexpr Instruction(Op opcode, size_t method_id, bool result_is_object, + inline Instruction(Op opcode, size_t index_argument, bool result_is_object, std::optional<const Value> dest, T... args) : opcode_{opcode}, - method_id_{method_id}, + index_argument_{index_argument}, result_is_object_{result_is_object}, dest_{dest}, args_{args...} {} const Op opcode_; // The index of the method to invoke, for kInvokeVirtual and similar opcodes. - const size_t method_id_{0}; + const size_t index_argument_{0}; const bool result_is_object_; const std::optional<const Value> dest_; const std::vector<const Value> args_; @@ -319,6 +349,7 @@ class MethodBuilder { void EncodeBranch(art::Instruction::Code op, const Instruction& instruction); void EncodeNew(const Instruction& instruction); void EncodeCast(const Instruction& instruction); + void EncodeFieldOp(const Instruction& instruction); // Low-level instruction format encoding. See // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of @@ -351,6 +382,14 @@ class MethodBuilder { buffer_.push_back(b); } + inline void Encode22c(art::Instruction::Code opcode, uint8_t a, uint8_t b, uint16_t c) { + // b|a|op|bbbb + CHECK(IsShortRegister(a)); + CHECK(IsShortRegister(b)); + buffer_.push_back((b << 12) | (a << 8) | opcode); + buffer_.push_back(c); + } + inline void Encode32x(art::Instruction::Code opcode, uint16_t a, uint16_t b) { buffer_.push_back(opcode); buffer_.push_back(a); @@ -481,6 +520,11 @@ class DexBuilder { // See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare // imported classes. ir::Type* GetOrAddType(const std::string& descriptor); + inline ir::Type* GetOrAddType(TypeDescriptor descriptor) { + return GetOrAddType(descriptor.descriptor()); + } + + ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type); // Returns the method id for the method, creating it if it has not been created yet. const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name, @@ -526,6 +570,9 @@ class DexBuilder { // Keep track of already-encoded protos. std::map<Prototype, ir::Proto*> proto_map_; + + // Keep track of fields that have been declared + std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_; }; template <typename... T> diff --git a/startop/view_compiler/dex_builder_test.cc b/startop/view_compiler/dex_builder_test.cc deleted file mode 100644 index 90c256f271cf..000000000000 --- a/startop/view_compiler/dex_builder_test.cc +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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. - */ - -#include "dex_builder.h" - -#include "dex/art_dex_file_loader.h" -#include "dex/dex_file.h" -#include "gtest/gtest.h" - -using namespace startop::dex; - -// Takes a DexBuilder, encodes it into an in-memory DEX file, verifies the resulting DEX file and -// returns whether the verification was successful. -bool EncodeAndVerify(DexBuilder* dex_file) { - slicer::MemView image{dex_file->CreateImage()}; - - art::ArtDexFileLoader loader; - std::string error_msg; - std::unique_ptr<const art::DexFile> loaded_dex_file{loader.Open(image.ptr<const uint8_t>(), - image.size(), - /*location=*/"", - /*location_checksum=*/0, - /*oat_dex_file=*/nullptr, - /*verify=*/true, - /*verify_checksum=*/false, - &error_msg)}; - return loaded_dex_file != nullptr; -} - -// Write out and verify a DEX file that corresponds to: -// -// package dextest; -// public class DexTest { -// public static void foo() {} -// } -TEST(DexBuilderTest, VerifyDexWithClassMethod) { - DexBuilder dex_file; - - auto cbuilder{dex_file.MakeClass("dextest.DexTest")}; - - auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void()})}; - method.BuildReturn(); - method.Encode(); - - EXPECT_TRUE(EncodeAndVerify(&dex_file)); -} - -// Makes sure a bad DEX class fails to verify. -TEST(DexBuilderTest, VerifyBadDexWithClassMethod) { - DexBuilder dex_file; - - auto cbuilder{dex_file.MakeClass("dextest.DexTest")}; - - // This method has the error, because methods cannot take Void() as a parameter. - auto method{ - cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void(), TypeDescriptor::Void()})}; - method.BuildReturn(); - method.Encode(); - - EXPECT_FALSE(EncodeAndVerify(&dex_file)); -} - -// Write out and verify a DEX file that corresponds to: -// -// package dextest; -// public class DexTest { -// public static int foo() { return 5; } -// } -TEST(DexBuilderTest, VerifyDexReturn5) { - DexBuilder dex_file; - - auto cbuilder{dex_file.MakeClass("dextest.DexTest")}; - - auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})}; - auto r = method.MakeRegister(); - method.BuildConst4(r, 5); - method.BuildReturn(r); - method.Encode(); - - EXPECT_TRUE(EncodeAndVerify(&dex_file)); -} - -// Write out and verify a DEX file that corresponds to: -// -// package dextest; -// public class DexTest { -// public static int foo(int x) { return x; } -// } -TEST(DexBuilderTest, VerifyDexReturnIntParam) { - DexBuilder dex_file; - - auto cbuilder{dex_file.MakeClass("dextest.DexTest")}; - - auto method{ - cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})}; - method.BuildReturn(Value::Parameter(0)); - method.Encode(); - - EXPECT_TRUE(EncodeAndVerify(&dex_file)); -} - -// Write out and verify a DEX file that corresponds to: -// -// package dextest; -// public class DexTest { -// public static int foo(String s) { return s.length(); } -// } -TEST(DexBuilderTest, VerifyDexCallStringLength) { - DexBuilder dex_file; - - auto cbuilder{dex_file.MakeClass("dextest.DexTest")}; - - MethodBuilder method{cbuilder.CreateMethod( - "foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::FromClassname("java.lang.String")})}; - - Value result = method.MakeRegister(); - - MethodDeclData string_length = - dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"), - "length", - Prototype{TypeDescriptor::Int()}); - - method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0))); - method.BuildReturn(result); - - method.Encode(); - - EXPECT_TRUE(EncodeAndVerify(&dex_file)); -} - -// Write out and verify a DEX file that corresponds to: -// -// package dextest; -// public class DexTest { -// public static int foo(String s) { return s.length(); } -// } -TEST(DexBuilderTest, VerifyDexCallManyRegisters) { - DexBuilder dex_file; - - auto cbuilder{dex_file.MakeClass("dextest.DexTest")}; - - MethodBuilder method{cbuilder.CreateMethod( - "foo", Prototype{TypeDescriptor::Int()})}; - - Value result = method.MakeRegister(); - - // Make a bunch of registers - for (size_t i = 0; i < 25; ++i) { - method.MakeRegister(); - } - - // Now load a string literal into a register - Value string_val = method.MakeRegister(); - method.BuildConstString(string_val, "foo"); - - MethodDeclData string_length = - dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"), - "length", - Prototype{TypeDescriptor::Int()}); - - method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, string_val)); - method.BuildReturn(result); - - method.Encode(); - - EXPECT_TRUE(EncodeAndVerify(&dex_file)); -} diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp index ac60e966fe43..9ad1ca1ef48e 100644 --- a/startop/view_compiler/dex_builder_test/Android.bp +++ b/startop/view_compiler/dex_builder_test/Android.bp @@ -39,6 +39,7 @@ android_test { srcs: [ "src/android/startop/test/DexBuilderTest.java", "src/android/startop/test/LayoutCompilerTest.java", + "src/android/startop/test/TestClass.java", ], sdk_version: "current", data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"], diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java index 42d4161ee81e..93496d01144f 100644 --- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java @@ -28,10 +28,10 @@ import org.junit.Test; // Adding tests here requires changes in several other places. See README.md in // the view_compiler directory for more information. -public class DexBuilderTest { +public final class DexBuilderTest { static ClassLoader loadDexFile(String filename) throws Exception { return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename, - ClassLoader.getSystemClassLoader()); + DexBuilderTest.class.getClassLoader()); } public void hello() {} @@ -171,4 +171,44 @@ public class DexBuilderTest { } Assert.assertTrue(castFailed); } + + @Test + public void readStaticField() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("readStaticField"); + TestClass.staticInteger = 5; + Assert.assertEquals(5, method.invoke(null)); + } + + @Test + public void setStaticField() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("setStaticField"); + TestClass.staticInteger = 5; + method.invoke(null); + Assert.assertEquals(7, TestClass.staticInteger); + } + + @Test + public void readInstanceField() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("readInstanceField", TestClass.class); + TestClass obj = new TestClass(); + obj.instanceField = 5; + Assert.assertEquals(5, method.invoke(null, obj)); + } + + @Test + public void setInstanceField() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("setInstanceField", TestClass.class); + TestClass obj = new TestClass(); + obj.instanceField = 5; + method.invoke(null, obj); + Assert.assertEquals(7, obj.instanceField); + } } diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java new file mode 100644 index 000000000000..dd7792306030 --- /dev/null +++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java @@ -0,0 +1,23 @@ +/* + * 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 android.startop.test; + + /** + * A simple class to help test DexBuilder. + */ +public final class TestClass { + public static int staticInteger; + + public int instanceField; +} diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc index c68793d10399..8febfb71ecd1 100644 --- a/startop/view_compiler/dex_layout_compiler.cc +++ b/startop/view_compiler/dex_layout_compiler.cc @@ -23,25 +23,6 @@ namespace startop { using android::base::StringPrintf; -void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) { - if (0 == name.compare(u"merge")) { - message_ = "Merge tags are not supported"; - can_compile_ = false; - } - if (0 == name.compare(u"include")) { - message_ = "Include tags are not supported"; - can_compile_ = false; - } - if (0 == name.compare(u"view")) { - message_ = "View tags are not supported"; - can_compile_ = false; - } - if (0 == name.compare(u"fragment")) { - message_ = "Fragment tags are not supported"; - can_compile_ = false; - } -} - DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method) : method_{method}, context_{dex::Value::Parameter(0)}, diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc index f62ec5dde85e..6dedf24e290d 100644 --- a/startop/view_compiler/dex_testcase_generator.cc +++ b/startop/view_compiler/dex_testcase_generator.cc @@ -282,6 +282,62 @@ void GenerateSimpleTestCases(const string& outdir) { method.Encode(); }(castObjectToString); + TypeDescriptor test_class = TypeDescriptor::FromClassname("android.startop.test.TestClass"); + + // Read a static field + // int readStaticField() { return TestClass.staticInteger; } + MethodBuilder readStaticField{ + cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})}; + [&](MethodBuilder& method) { + const ir::FieldDecl* field = + dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int()); + Value result{method.MakeRegister()}; + method.AddInstruction(Instruction::GetStaticField(field->orig_index, result)); + method.BuildReturn(result, /*is_object=*/false); + method.Encode(); + }(readStaticField); + + // Set a static field + // void setStaticField() { TestClass.staticInteger = 7; } + MethodBuilder setStaticField{ + cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})}; + [&](MethodBuilder& method) { + const ir::FieldDecl* field = + dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int()); + Value number{method.MakeRegister()}; + method.BuildConst4(number, 7); + method.AddInstruction(Instruction::SetStaticField(field->orig_index, number)); + method.BuildReturn(); + method.Encode(); + }(setStaticField); + + // Read an instance field + // int readInstanceField(TestClass obj) { return obj.instanceField; } + MethodBuilder readInstanceField{ + cbuilder.CreateMethod("readInstanceField", Prototype{TypeDescriptor::Int(), test_class})}; + [&](MethodBuilder& method) { + const ir::FieldDecl* field = + dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int()); + Value result{method.MakeRegister()}; + method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0))); + method.BuildReturn(result, /*is_object=*/false); + method.Encode(); + }(readInstanceField); + + // Set an instance field + // void setInstanceField(TestClass obj) { obj.instanceField = 7; } + MethodBuilder setInstanceField{ + cbuilder.CreateMethod("setInstanceField", Prototype{TypeDescriptor::Void(), test_class})}; + [&](MethodBuilder& method) { + const ir::FieldDecl* field = + dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int()); + Value number{method.MakeRegister()}; + method.BuildConst4(number, 7); + method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number)); + method.BuildReturn(); + method.Encode(); + }(setInstanceField); + slicer::MemView image{dex_file.CreateImage()}; std::ofstream out_file(outdir + "/simple.dex"); out_file.write(image.ptr<const char>(), image.size()); diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 83aa52176b87..42eaa8d8d71c 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -2975,6 +2975,7 @@ public final class Telephony { * The {@code content://} style URL for this table. Can be appended with a part ID to * address individual parts. */ + @NonNull public static final Uri CONTENT_URI = Uri.withAppendedPath(Mms.CONTENT_URI, "part"); /** @@ -3158,6 +3159,8 @@ public final class Telephony { /** * The {@code content://} style URL for locked messages in this table. + * <P>This {@link Uri} is used to check at most one locked message found in the union of MMS + * and SMS messages. Also this will return only _id column in response.</P> */ public static final Uri CONTENT_LOCKED_URI = Uri.parse( "content://mms-sms/locked"); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 31cd60807570..44c50f0a288b 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -852,6 +852,19 @@ public class CarrierConfigManager { "carrier_metered_roaming_apn_types_strings"; /** + * APN types that are not allowed on cellular + * @hide + */ + public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY = + "carrier_wwan_disallowed_apn_types_string_array"; + + /** + * APN types that are not allowed on IWLAN + * @hide + */ + public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY = + "carrier_wlan_disallowed_apn_types_string_array"; + /** * CDMA carrier ERI (Enhanced Roaming Indicator) file name * @hide */ @@ -2377,6 +2390,14 @@ public class CarrierConfigManager { public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool"; + /** + * Determines whether we should show a notification when the phone established a data + * connection in roaming network, to warn users about possible roaming charges. + * @hide + */ + public static final String KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL = + "show_data_connected_roaming_notification"; + /** * A list of 4 LTE RSRP thresholds above which a signal level is considered POOR, * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting. @@ -2804,6 +2825,23 @@ public class CarrierConfigManager { "is_opportunistic_subscription_bool"; /** + * Configs used by the IMS stack. + */ + public static final class Ims { + /** Prefix of all Ims.KEY_* constants. */ + public static final String KEY_PREFIX = "ims."; + + //TODO: Add configs related to IMS. + + private Ims() {} + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + return defaults; + } + } + + /** * A list of 4 GSM RSSI thresholds above which a signal level is considered POOR, * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting. * @@ -2817,6 +2855,15 @@ public class CarrierConfigManager { public static final String KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY = "gsm_rssi_thresholds_int_array"; + /** + * Determines whether Wireless Priority Service call is supported over IMS. + * + * See Wireless Priority Service from https://www.fcc.gov/general/wireless-priority-service-wps + * @hide + */ + public static final String KEY_SUPPORT_WPS_OVER_IMS_BOOL = + "support_wps_over_ims_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -2951,6 +2998,10 @@ public class CarrierConfigManager { new String[]{"default", "mms", "dun", "supl"}); sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS, new String[]{"default", "mms", "dun", "supl"}); + sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY, + new String[]{""}); + sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY, + new String[]{""}); sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY, new int[]{ 4, /* IS95A */ @@ -3157,6 +3208,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, ""); sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false); sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false); + sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false); sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY, new int[] { -128, /* SIGNAL_STRENGTH_POOR */ @@ -3217,6 +3269,8 @@ public class CarrierConfigManager { -97, /* SIGNAL_STRENGTH_GOOD */ -89, /* SIGNAL_STRENGTH_GREAT */ }); + sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true); + sDefaults.putAll(Ims.getDefaults()); } /** @@ -3413,4 +3467,75 @@ public class CarrierConfigManager { return ICarrierConfigLoader.Stub .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE)); } + + /** + * Gets the configuration values for a component using its prefix. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param prefix prefix of the component. + * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}. + * + * @see #getConfigForSubId + */ + @Nullable + public PersistableBundle getConfigByComponentForSubId(String prefix, int subId) { + PersistableBundle configs = getConfigForSubId(subId); + + if (configs == null) { + return null; + } + + PersistableBundle ret = new PersistableBundle(); + for (String configKey : configs.keySet()) { + if (configKey.startsWith(prefix)) { + addConfig(configKey, configs.get(configKey), ret); + } + } + + return ret; + } + + private void addConfig(String key, Object value, PersistableBundle configs) { + if (value instanceof String) { + configs.putString(key, (String) value); + } + + if (value instanceof String[]) { + configs.putStringArray(key, (String[]) value); + } + + if (value instanceof Integer) { + configs.putInt(key, (Integer) value); + } + + if (value instanceof Long) { + configs.putLong(key, (Long) value); + } + + if (value instanceof Double) { + configs.putDouble(key, (Double) value); + } + + if (value instanceof Boolean) { + configs.putBoolean(key, (Boolean) value); + } + + if (value instanceof int[]) { + configs.putIntArray(key, (int[]) value); + } + + if (value instanceof double[]) { + configs.putDoubleArray(key, (double[]) value); + } + + if (value instanceof boolean[]) { + configs.putBooleanArray(key, (boolean[]) value); + } + + if (value instanceof long[]) { + configs.putLongArray(key, (long[]) value); + } + } } diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 127eabd701ff..31b3a0523b7c 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -143,7 +143,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P } /** - * Get the signal strength as dBm + * Get the signal strength as dBm. */ @Override public int getDbm() { @@ -163,18 +163,17 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P } /** - * Return the Received Signal Strength Indicator + * Return the Received Signal Strength Indicator. * * @return the RSSI in dBm (-113, -51) or * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. - * @hide */ public int getRssi() { return mRssi; } /** - * Return the Bit Error Rate + * Return the Bit Error Rate. * * @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index b75e51577fdb..f03a9dc0f963 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -22,9 +22,11 @@ import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; import android.annotation.IntDef; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.database.Cursor; import android.location.CountryDetector; import android.net.Uri; @@ -164,6 +166,33 @@ public class PhoneNumberUtils { return c == 'w'||c == 'W'; } + private static int sMinMatch = 0; + + private static int getMinMatch() { + if (sMinMatch == 0) { + sMinMatch = Resources.getSystem().getInteger( + com.android.internal.R.integer.config_phonenumber_compare_min_match); + } + return sMinMatch; + } + + /** + * A Test API to get current sMinMatch. + * @hide + */ + @TestApi + public static int getMinMatchForTest() { + return getMinMatch(); + } + + /** + * A Test API to set sMinMatch. + * @hide + */ + @TestApi + public static void setMinMatchForTest(int minMatch) { + sMinMatch = minMatch; + } /** Returns true if ch is not dialable or alpha char */ private static boolean isSeparator(char ch) { @@ -475,7 +504,7 @@ public class PhoneNumberUtils { * enough for caller ID purposes. * * - Compares from right to left - * - requires MIN_MATCH (7) characters to match + * - requires minimum characters to match * - handles common trunk prefixes and international prefixes * (basically, everything except the Russian trunk prefix) * @@ -491,6 +520,7 @@ public class PhoneNumberUtils { int matched; int numNonDialableCharsInA = 0; int numNonDialableCharsInB = 0; + int minMatch = getMinMatch(); if (a == null || b == null) return a == b; @@ -530,12 +560,12 @@ public class PhoneNumberUtils { } } - if (matched < MIN_MATCH) { + if (matched < minMatch) { int effectiveALen = a.length() - numNonDialableCharsInA; int effectiveBLen = b.length() - numNonDialableCharsInB; - // if the number of dialable chars in a and b match, but the matched chars < MIN_MATCH, + // if the number of dialable chars in a and b match, but the matched chars < minMatch, // treat them as equal (i.e. 404-04 and 40404) if (effectiveALen == effectiveBLen && effectiveALen == matched) { return true; @@ -545,7 +575,7 @@ public class PhoneNumberUtils { } // At least one string has matched completely; - if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) { + if (matched >= minMatch && (ia < 0 || ib < 0)) { return true; } @@ -736,7 +766,7 @@ public class PhoneNumberUtils { } /** - * Returns the rightmost MIN_MATCH (5) characters in the network portion + * Returns the rightmost minimum matched characters in the network portion * in *reversed* order * * This can be used to do a database lookup against the column @@ -747,7 +777,7 @@ public class PhoneNumberUtils { public static String toCallerIDMinMatch(String phoneNumber) { String np = extractNetworkPortionAlt(phoneNumber); - return internalGetStrippedReversed(np, MIN_MATCH); + return internalGetStrippedReversed(np, getMinMatch()); } /** @@ -1709,26 +1739,6 @@ public class PhoneNumberUtils { return normalizedDigits.toString(); } - // Three and four digit phone numbers for either special services, - // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should - // not match. - // - // This constant used to be 5, but SMS short codes has increased in length and - // can be easily 6 digits now days. Most countries have SMS short code length between - // 3 to 6 digits. The exceptions are - // - // Australia: Short codes are six or eight digits in length, starting with the prefix "19" - // followed by an additional four or six digits and two. - // Czechia: Codes are seven digits in length for MO and five (not billed) or - // eight (billed) for MT direction - // - // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference - // - // However, in order to loose match 650-555-1212 and 555-1212, we need to set the min match - // to 7. - @UnsupportedAppUsage - static final int MIN_MATCH = 7; - /** * Checks a given number against the list of * emergency numbers provided by the RIL and SIM card. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index fe812151489b..d72ca6bec189 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2327,6 +2327,10 @@ public class TelephonyManager { * <p> * The ISO-3166 country code is provided in lowercase 2 character format. * <p> + * Note: In multi-sim, this returns a shared emergency network country iso from other + * subscription if the subscription used to create the TelephonyManager doesn't camp on + * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding + * slot. * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine * if on a CDMA network). * <p> diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index aaa98eb7bd59..733fb1e59a85 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -22,6 +22,7 @@ import android.hardware.radio.V1_4.EmergencyNumberSource; import android.hardware.radio.V1_4.EmergencyServiceCategory; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.CarrierConfigManager; import android.telephony.PhoneNumberUtils; import android.telephony.Rlog; @@ -301,6 +302,9 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu * The character in the number string is only the dial pad * character('0'-'9', '*', '+', or '#'). For example: 911. * + * If the number starts with carrier prefix, the carrier prefix is configured in + * {@link CarrierConfigManager#KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY}. + * * @return the dialing number. */ public @NonNull String getNumber() { diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java index 8c686f704967..8e1324b3be0b 100644 --- a/telephony/java/android/telephony/ims/ImsException.java +++ b/telephony/java/android/telephony/ims/ImsException.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.pm.PackageManager; +import android.telephony.SubscriptionManager; import android.text.TextUtils; import java.lang.annotation.Retention; @@ -55,12 +56,23 @@ public final class ImsException extends Exception { */ public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; + /** + * The subscription ID associated with this operation is invalid or not active. + * <p> + * This is a configuration error and there should be no retry. The subscription used for this + * operation is either invalid or has become inactive. The active subscriptions can be queried + * with {@link SubscriptionManager#getActiveSubscriptionInfoList()}. + * @hide + */ + public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3; + /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "CODE_ERROR_", value = { CODE_ERROR_UNSPECIFIED, CODE_ERROR_SERVICE_UNAVAILABLE, - CODE_ERROR_UNSUPPORTED_OPERATION + CODE_ERROR_UNSUPPORTED_OPERATION, + CODE_ERROR_INVALID_SUBSCRIPTION }) public @interface ImsErrorCode {} diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index be5872387d7b..a1a7fcc5dd51 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -31,6 +31,7 @@ import android.net.Uri; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.telephony.AccessNetworkConstants; import android.telephony.SubscriptionManager; import android.telephony.ims.aidl.IImsCapabilityCallback; @@ -375,6 +376,13 @@ public class ImsMmTelManager { c.setExecutor(executor); try { getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder()); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -390,8 +398,6 @@ public class ImsMmTelManager { * @param c The {@link RegistrationCallback} to be removed. * @see SubscriptionManager.OnSubscriptionsChangedListener * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) - * @throws IllegalArgumentException if the subscription ID associated with this callback is - * invalid. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) { @@ -445,6 +451,13 @@ public class ImsMmTelManager { c.setExecutor(executor); try { getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder()); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (IllegalStateException e) { @@ -460,8 +473,6 @@ public class ImsMmTelManager { * inactive subscription, it will result in a no-op. * @param c The MmTel {@link CapabilityCallback} to be removed. * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) - * @throws IllegalArgumentException if the subscription ID associated with this callback is - * invalid. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) { @@ -482,12 +493,9 @@ public class ImsMmTelManager { * be enabled as long as the carrier has provisioned these services for the specified * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on * carrier requirements. - * - * Modifying this value may also trigger an IMS registration or deregistration, depending on - * whether or not the new value is enabled or disabled. - * + * <p> * Note: If the carrier configuration for advanced calling is not editable or hidden, this - * method will do nothing and will instead always use the default value. + * method will always return the default value. * * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL @@ -495,12 +503,21 @@ public class ImsMmTelManager { * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL * @see #setAdvancedCallingSettingEnabled(boolean) + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user's setting for advanced calling is enabled, false otherwise. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled() { try { return getITelephony().isAdvancedCallingSettingEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -526,12 +543,20 @@ public class ImsMmTelManager { * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL * @see #isAdvancedCallingSettingEnabled() + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean isEnabled) { try { getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -597,6 +622,9 @@ public class ImsMmTelManager { /** * The user's setting for whether or not they have enabled the "Video Calling" setting. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user’s “Video Calling” setting is currently enabled. * @see #setVtSettingEnabled(boolean) */ @@ -604,6 +632,13 @@ public class ImsMmTelManager { public boolean isVtSettingEnabled() { try { return getITelephony().isVtSettingEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -611,13 +646,22 @@ public class ImsMmTelManager { /** * Change the user's setting for Video Telephony and enable the Video Telephony capability. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #isVtSettingEnabled() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean isEnabled) { try { getITelephony().setVtSettingEnabled(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -625,12 +669,22 @@ public class ImsMmTelManager { /** * @return true if the user's setting for Voice over WiFi is enabled and false if it is not. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled() { try { return getITelephony().isVoWiFiSettingEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -638,6 +692,9 @@ public class ImsMmTelManager { /** * Sets the user's setting for whether or not Voice over WiFi is enabled. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise= * @see #isVoWiFiSettingEnabled() */ @@ -645,13 +702,23 @@ public class ImsMmTelManager { public void setVoWiFiSettingEnabled(boolean isEnabled) { try { getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** + * Returns the user's voice over WiFi roaming setting associated with the current subscription. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user's setting for Voice over WiFi while roaming is enabled, false * if disabled. * @see #setVoWiFiRoamingSettingEnabled(boolean) @@ -660,6 +727,13 @@ public class ImsMmTelManager { public boolean isVoWiFiRoamingSettingEnabled() { try { return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -667,15 +741,24 @@ public class ImsMmTelManager { /** * Change the user's setting for Voice over WiFi while roaming. + * * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled, * false otherwise. + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #isVoWiFiRoamingSettingEnabled() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) { try { getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -691,19 +774,31 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean isCapable, int mode) { try { getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** + * Returns the user's voice over WiFi Roaming mode setting associated with the device. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @return The Voice over WiFi Mode preference set by the user, which can be one of the * following: * - {@link #WIFI_MODE_WIFI_ONLY} @@ -715,6 +810,13 @@ public class ImsMmTelManager { public @WiFiCallingMode int getVoWiFiModeSetting() { try { return getITelephony().getVoWiFiModeSetting(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -727,13 +829,21 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #getVoWiFiModeSetting() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(@WiFiCallingMode int mode) { try { getITelephony().setVoWiFiModeSetting(mSubId, mode); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -748,12 +858,21 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiRoamingSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @WiFiCallingMode int getVoWiFiRoamingModeSetting() { try { return getITelephony().getVoWiFiRoamingModeSetting(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -768,13 +887,21 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #getVoWiFiRoamingModeSetting() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) { try { getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -787,13 +914,21 @@ public class ImsMmTelManager { * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting * for RTT. That value is enabled/disabled separately by the user through the Accessibility * settings. + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @param isEnabled if true RTT should be enabled during calls made on this subscription. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean isEnabled) { try { getITelephony().setRttCapabilitySetting(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -801,6 +936,9 @@ public class ImsMmTelManager { /** * @return true if TTY over VoLTE is supported + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see android.telecom.TelecomManager#getCurrentTtyMode * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL */ @@ -808,6 +946,13 @@ public class ImsMmTelManager { boolean isTtyOverVolteEnabled() { try { return getITelephony().isTtyOverVolteEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java new file mode 100644 index 000000000000..3c343dd19a86 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -0,0 +1,234 @@ +/* + * 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 android.telephony.ims; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.content.Context; +import android.os.Binder; +import android.telephony.SubscriptionManager; +import android.telephony.ims.aidl.IImsCapabilityCallback; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; + +import java.util.concurrent.Executor; + +/** + * Manager for interfacing with the framework RCS services, including the User Capability Exchange + * (UCE) service, as well as managing user settings. + * + * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this manager. + * @hide + */ +public class ImsRcsManager { + + /** + * Receives RCS availability status updates from the ImsService. + * + * @see #isAvailable(int) + * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) + * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + */ + public static class AvailabilityCallback { + + private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + + private final AvailabilityCallback mLocalCallback; + private Executor mExecutor; + + CapabilityBinder(AvailabilityCallback c) { + mLocalCallback = c; + } + + @Override + public void onCapabilitiesStatusChanged(int config) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged( + new RcsFeature.RcsImsCapabilities(config)))); + } + + @Override + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + // This is not used for public interfaces. + } + + @Override + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + @ImsFeature.ImsCapabilityError int reason) { + // This is not used for public interfaces + } + + private void setExecutor(Executor executor) { + mExecutor = executor; + } + } + + private final CapabilityBinder mBinder = new CapabilityBinder(this); + + /** + * The availability of the feature's capabilities has changed to either available or + * unavailable. + * <p> + * If unavailable, the feature does not support the capability at the current time. This may + * be due to network or subscription provisioning changes, such as the IMS registration + * being lost, network type changing, or OMA-DM provisioning updates. + * + * @param capabilities The new availability of the capabilities. + */ + public void onAvailabilityChanged(RcsFeature.RcsImsCapabilities capabilities) { + } + + /**@hide*/ + public final IImsCapabilityCallback getBinder() { + return mBinder; + } + + private void setExecutor(Executor executor) { + mBinder.setExecutor(executor); + } + } + + private final int mSubId; + private final Context mContext; + + + /** + * Create an instance of ImsRcsManager for the subscription id specified. + * + * @param context The context to create this ImsRcsManager instance within. + * @param subscriptionId The ID of the subscription that this ImsRcsManager will use. + * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList() + * @throws IllegalArgumentException if the subscription is invalid. + * @hide + */ + public static ImsRcsManager createForSubscriptionId(Context context, int subscriptionId) { + if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("Invalid subscription ID"); + } + + return new ImsRcsManager(context, subscriptionId); + } + + /** + * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this class. + */ + private ImsRcsManager(Context context, int subId) { + mContext = context; + mSubId = subId; + } + + /** + * Registers an {@link AvailabilityCallback} with the system, which will provide RCS + * availability updates for the subscription specified. + * + * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to + * subscription changed events and call + * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a + * subscription is removed. + * <p> + * When the callback is registered, it will initiate the callback c to be called with the + * current capabilities. + * + * @param executor The executor the callback events should be run on. + * @param c The RCS {@link AvailabilityCallback} to be registered. + * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * @throws ImsException if the subscription associated with this instance of + * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void registerRcsAvailabilityCallback(@CallbackExecutor Executor executor, + @NonNull AvailabilityCallback c) throws ImsException { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + c.setExecutor(executor); + throw new UnsupportedOperationException("registerRcsAvailabilityCallback is not" + + "supported."); + } + + /** + * Removes an existing RCS {@link AvailabilityCallback}. + * <p> + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be unregistered. If this method is called for an + * inactive subscription, it will result in a no-op. + * @param c The RCS {@link AvailabilityCallback} to be removed. + * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + } + throw new UnsupportedOperationException("unregisterRcsAvailabilityCallback is not" + + "supported."); + } + + /** + * Query for the capability of an IMS RCS service provided by the framework. + * <p> + * This only reports the status of RCS capabilities provided by the framework, not necessarily + * RCS capabilities provided over-the-top by applications. + * + * @param capability The RCS capability to query. + * @return true if the RCS capability is capable for this subscription, false otherwise. This + * does not necessarily mean that we are registered for IMS and the capability is available, but + * rather the subscription is capable of this service over IMS. + * @see #isAvailable(int) + * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) { + throw new UnsupportedOperationException("isCapable is not supported."); + } + + /** + * Query the availability of an IMS RCS capability. + * <p> + * This only reports the status of RCS capabilities provided by the framework, not necessarily + * RCS capabilities provided by over-the-top by applications. + * + * @param capability the RCS capability to query. + * @return true if the RCS capability is currently available for the associated subscription, + * false otherwise. If the capability is available, IMS is registered and the service is + * currently available over IMS. + * @see #isCapable(int) + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) { + throw new UnsupportedOperationException("isAvailable is not supported."); + } + + /** + * @return A new {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for + * this subscription. + */ + @NonNull + public RcsUceAdapter getUceAdapter() { + return new RcsUceAdapter(mSubId); + } +} diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index cc037e3ea814..effdf48067c3 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -387,6 +387,24 @@ public class ProvisioningManager { } } + /** + * Notify the framework that an RCS autoconfiguration XML file has been received for + * provisioning. + * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed. + * @param isCompressed The XML file is compressed in gzip format and must be decompressed + * before being read. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { + if (config == null) { + throw new IllegalArgumentException("Must include a non-null config XML file."); + } + // TODO: Connect to ImsConfigImplBase. + throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not" + + "supported"); + } + private static boolean isImsAvailableOnDevice() { IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); if (pm == null) { diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl index f04360fc1942..bef6a4037fea 100644 --- a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -14,16 +14,7 @@ * limitations under the License. */ -package com.android.preload.classdataretrieval; -import com.android.ddmlib.Client; +package android.telephony.ims; -import java.util.Map; - -/** - * Retrieve a class-to-classloader map for loaded classes from the client. - */ -public interface ClassDataRetriever { - - public Map<String, String> getClassData(Client client); -} +parcelable RcsContactUceCapability; diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java new file mode 100644 index 000000000000..492170b1069a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -0,0 +1,291 @@ +/* + * 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 android.telephony.ims; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Contains the User Capability Exchange capabilities corresponding to a contact's URI. + * @hide + */ +public final class RcsContactUceCapability implements Parcelable { + + /** Supports 1-to-1 chat */ + public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0); + /** Supports group chat */ + public static final int CAPABILITY_CHAT_SESSION = (1 << 1); + /** Supports full store and forward group chat information. */ + public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2); + /** + * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward. + */ + public static final int CAPABILITY_FILE_TRANSFER = (1 << 3); + /** Supports File Transfer Thumbnail */ + public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4); + /** Supports File Transfer with Store and Forward */ + public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5); + /** Supports File Transfer via HTTP */ + public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6); + /** Supports file transfer via SMS */ + public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7); + /** Supports image sharing */ + public static final int CAPABILITY_IMAGE_SHARE = (1 << 8); + /** Supports video sharing during a circuit-switch call (IR.74)*/ + public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9); + /** Supports video share outside of voice call (IR.84) */ + public static final int CAPABILITY_VIDEO_SHARE = (1 << 10); + /** Supports social presence information */ + public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11); + /** Supports capability discovery via presence */ + public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12); + /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */ + public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13); + /** Supports IP video calling (IR.94) */ + public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14); + /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */ + public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15); + /** Supports Geolocation PUSH via SMS for fallback. */ + public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16); + /** Supports Geolocation pull. */ + public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17); + /** Supports Geolocation pull using file transfer support. */ + public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18); + /** Supports RCS voice calling */ + public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19); + /** Supports RCS video calling */ + public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20); + /** Supports RCS video calling, where video media can not be dropped */ + public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21); + + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_", flag = true, value = { + CAPABILITY_CHAT_STANDALONE, + CAPABILITY_CHAT_SESSION, + CAPABILITY_CHAT_SESSION_STORE_FORWARD, + CAPABILITY_FILE_TRANSFER, + CAPABILITY_FILE_TRANSFER_THUMBNAIL, + CAPABILITY_FILE_TRANSFER_STORE_FORWARD, + CAPABILITY_FILE_TRANSFER_HTTP, + CAPABILITY_FILE_TRANSFER_SMS, + CAPABILITY_IMAGE_SHARE, + CAPABILITY_VIDEO_SHARE_DURING_CS_CALL, + CAPABILITY_VIDEO_SHARE, + CAPABILITY_SOCIAL_PRESENCE, + CAPABILITY_DISCOVERY_VIA_PRESENCE, + CAPABILITY_IP_VOICE_CALL, + CAPABILITY_IP_VIDEO_CALL, + CAPABILITY_GEOLOCATION_PUSH, + CAPABILITY_GEOLOCATION_PUSH_SMS, + CAPABILITY_GEOLOCATION_PULL, + CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER, + CAPABILITY_RCS_VOICE_CALL, + CAPABILITY_RCS_VIDEO_CALL, + CAPABILITY_RCS_VIDEO_ONLY_CALL + }) + public @interface CapabilityFlag {} + + /** + * Builder to help construct {@link RcsContactUceCapability} instances. + */ + public static class Builder { + + private final RcsContactUceCapability mCapabilities; + + /** + * Create the Builder, which can be used to set UCE capabilities as well as custom + * capability extensions. + * @param contact The contact URI that the capabilities are attached to. + */ + public Builder(@NonNull Uri contact) { + mCapabilities = new RcsContactUceCapability(contact); + } + + /** + * Add a UCE capability bit-field as well as the associated URI that the framework should + * use for those services. This is mainly used for capabilities that may use a URI separate + * from the contact's URI, for example the URI to use for VT calls. + * @param type The capability to map to a service URI that is different from the contact's + * URI. + */ + public Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) { + mCapabilities.mCapabilities |= type; + // Put each of these capabilities into the map separately. + for (int shift = 0; shift < Integer.SIZE; shift++) { + int cap = type & (1 << shift); + if (cap != 0) { + mCapabilities.mServiceMap.put(cap, serviceUri); + // remove that capability from the field. + type &= ~cap; + } + if (type == 0) { + // no need to keep going, end early. + break; + } + } + return this; + } + + /** + * Add a UCE capability flag that this contact supports. + * @param type the capability that the contact supports. + */ + public Builder add(@CapabilityFlag int type) { + mCapabilities.mCapabilities |= type; + return this; + } + + /** + * Add a carrier specific service tag. + * @param extension A string containing a carrier specific service tag that is an extension + * of the {@link CapabilityFlag}s that are defined here. + */ + public Builder add(@NonNull String extension) { + mCapabilities.mExtensionTags.add(extension); + return this; + } + + /** + * @return the constructed instance. + */ + public RcsContactUceCapability build() { + return mCapabilities; + } + } + + private final Uri mContactUri; + private int mCapabilities; + private List<String> mExtensionTags = new ArrayList<>(); + private Map<Integer, Uri> mServiceMap = new HashMap<>(); + + /** + * Use {@link Builder} to build an instance of this interface. + * @param contact The URI associated with this capability information. + * @hide + */ + RcsContactUceCapability(@NonNull Uri contact) { + mContactUri = contact; + } + + private RcsContactUceCapability(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mCapabilities = in.readInt(); + in.readStringList(mExtensionTags); + // read mServiceMap as key,value pair + int mapSize = in.readInt(); + for (int i = 0; i < mapSize; i++) { + mServiceMap.put(in.readInt(), in.readParcelable(Uri.class.getClassLoader())); + } + } + + public static final Creator<RcsContactUceCapability> CREATOR = + new Creator<RcsContactUceCapability>() { + @Override + public RcsContactUceCapability createFromParcel(Parcel in) { + return new RcsContactUceCapability(in); + } + + @Override + public RcsContactUceCapability[] newArray(int size) { + return new RcsContactUceCapability[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(mContactUri, 0); + out.writeInt(mCapabilities); + out.writeStringList(mExtensionTags); + // write mServiceMap as key,value pairs + int mapSize = mServiceMap.keySet().size(); + out.writeInt(mapSize); + for (int key : mServiceMap.keySet()) { + out.writeInt(key); + out.writeParcelable(mServiceMap.get(key), 0); + } + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Query for a capability + * @param type The capability flag to query. + * @return true if the capability flag specified is set, false otherwise. + */ + public boolean isCapable(@CapabilityFlag int type) { + return (mCapabilities & type) > 0; + } + + /** + * @return true if the extension service tag is set, false otherwise. + */ + public boolean isCapable(@NonNull String extensionTag) { + return mExtensionTags.contains(extensionTag); + } + + /** + * @return An immutable list containing all of the extension tags that have been set as capable. + * @throws UnsupportedOperationException if this list is modified. + */ + public @NonNull List<String> getCapableExtensionTags() { + return Collections.unmodifiableList(mExtensionTags); + } + + /** + * Retrieves the {@link Uri} associated with the capability being queried. + * <p> + * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless + * a different service {@link Uri} was associated with this capability using + * {@link Builder#add(int, Uri)}. + * + * @return a String containing the {@link Uri} associated with the service tag or + * {@code null} if this capability is not set as capable. + * @see #isCapable(int) + */ + public @Nullable Uri getServiceUri(@CapabilityFlag int type) { + Uri result = mServiceMap.getOrDefault(type, null); + // If the capability is capable, but does not have a service URI associated, use the default + // contact URI. + if (result == null) { + return isCapable(type) ? getContactUri() : null; + } + return result; + } + + /** + * @return the URI representing the contact associated with the capabilities. + */ + public @NonNull Uri getContactUri() { + return mContactUri; + } +} diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java new file mode 100644 index 000000000000..a6a7a84c2c65 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -0,0 +1,276 @@ +/* + * 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 android.telephony.ims; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.content.Context; +import android.net.Uri; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Manages RCS User Capability Exchange for the subscription specified. + * + * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class. + * @hide + */ +public class RcsUceAdapter { + + /** + * An unknown error has caused the request to fail. + */ + public static final int ERROR_GENERIC_FAILURE = 1; + /** + * The carrier network does not have UCE support enabled for this subscriber. + */ + public static final int ERROR_NOT_ENABLED = 2; + /** + * The data network that the device is connected to does not support UCE currently (e.g. it is + * 1x only currently). + */ + public static final int ERROR_NOT_AVAILABLE = 3; + /** + * The network has responded with SIP 403 error and a reason "User not registered." + */ + public static final int ERROR_NOT_REGISTERED = 4; + /** + * The network has responded to this request with a SIP 403 error and reason "not authorized for + * presence" for this subscriber. + */ + public static final int ERROR_NOT_AUTHORIZED = 5; + /** + * The network has responded to this request with a SIP 403 error and no reason. + */ + public static final int ERROR_FORBIDDEN = 6; + /** + * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS + * subscriber to the carrier network. + */ + public static final int ERROR_NOT_FOUND = 7; + /** + * The capabilities request contained too many URIs for the carrier network to handle. Retry + * with a lower number of contact numbers. The number varies per carrier. + */ + // TODO: Try to integrate this into the API so that the service will split based on carrier. + public static final int ERROR_REQUEST_TOO_LARGE = 8; + /** + * The network did not respond to the capabilities request before the request timed out. + */ + public static final int ERROR_REQUEST_TIMEOUT = 10; + /** + * The request failed due to the service having insufficient memory. + */ + public static final int ERROR_INSUFFICIENT_MEMORY = 11; + /** + * The network was lost while trying to complete the request. + */ + public static final int ERROR_LOST_NETWORK = 12; + /** + * The request has failed because the same request has already been added to the queue. + */ + public static final int ERROR_ALREADY_IN_QUEUE = 13; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "ERROR_", value = { + ERROR_GENERIC_FAILURE, + ERROR_NOT_ENABLED, + ERROR_NOT_AVAILABLE, + ERROR_NOT_REGISTERED, + ERROR_NOT_AUTHORIZED, + ERROR_FORBIDDEN, + ERROR_NOT_FOUND, + ERROR_REQUEST_TOO_LARGE, + ERROR_REQUEST_TIMEOUT, + ERROR_INSUFFICIENT_MEMORY, + ERROR_LOST_NETWORK, + ERROR_ALREADY_IN_QUEUE + }) + public @interface ErrorCode {} + + /** + * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for + * UCE. + */ + public static final int PUBLISH_STATE_200_OK = 1; + + /** + * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. + */ + public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; + + /** + * The device has tried to publish its capabilities, which has resulted in an error. This error + * is related to the fact that the device is not VoLTE provisioned. + */ + public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; + + /** + * The device has tried to publish its capabilities, which has resulted in an error. This error + * is related to the fact that the device is not RCS or UCE provisioned. + */ + public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; + + /** + * The last publish resulted in a "408 Request Timeout" response. + */ + public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; + + /** + * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable" + * or SIP 423 - "Interval too short". + * <p> + * Device shall retry with exponential back-off. + */ + public static final int PUBLISH_STATE_OTHER_ERROR = 6; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "PUBLISH_STATE_", value = { + PUBLISH_STATE_200_OK, + PUBLISH_STATE_NOT_PUBLISHED, + PUBLISH_STATE_VOLTE_PROVISION_ERROR, + PUBLISH_STATE_RCS_PROVISION_ERROR, + PUBLISH_STATE_REQUEST_TIMEOUT, + PUBLISH_STATE_OTHER_ERROR + }) + public @interface PublishState {} + + + /** + * Provides a one-time callback for the response to a UCE request. After this callback is called + * by the framework, the reference to this callback will be discarded on the service side. + * @see #requestCapabilities(Executor, List, CapabilitiesCallback) + */ + public static class CapabilitiesCallback { + + /** + * Notify this application that the pending capability request has returned successfully. + * @param contactCapabilities List of capabilities associated with each contact requested. + */ + public void onCapabilitiesReceived( + @NonNull List<RcsContactUceCapability> contactCapabilities) { + + } + + /** + * The pending request has resulted in an error and may need to be retried, depending on the + * error code. + * @param errorCode The reason for the framework being unable to process the request. + */ + public void onError(@ErrorCode int errorCode) { + + } + } + + private final int mSubId; + + /** + * Not to be instantiated directly, use + * {@link ImsRcsManager#createForSubscriptionId(Context, int)} and + * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class. + */ + RcsUceAdapter(int subId) { + mSubId = subId; + } + + /** + * Request the User Capability Exchange capabilities for one or more contacts. + * <p> + * Be sure to check the availability of this feature using + * {@link ImsRcsManager#isAvailable(int)} and ensuring + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else + * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. + * + * @param executor The executor that will be used when the request is completed and the + * {@link CapabilitiesCallback} is called. + * @param contactNumbers A list of numbers that the capabilities are being requested for. + * @param c A one-time callback for when the request for capabilities completes or there is an + * error processing the request. + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void requestCapabilities(@CallbackExecutor Executor executor, + @NonNull List<Uri> contactNumbers, + @NonNull CapabilitiesCallback c) throws ImsException { + throw new UnsupportedOperationException("isUceSettingEnabled is not supported."); + } + + /** + * Gets the last publish result from the UCE service if the device is using an RCS presence + * server. + * @return The last publish result from the UCE service. If the device is using SIP OPTIONS, + * this method will return {@link #PUBLISH_STATE_200_OK} as well. + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @PublishState int getUcePublishState() throws ImsException { + throw new UnsupportedOperationException("getPublishState is not supported."); + } + + /** + * The user’s setting for whether or not Presence and User Capability Exchange (UCE) is enabled + * for the associated subscription. + * + * @return true if the user’s setting for UCE is enabled, false otherwise. If false, + * {@link ImsRcsManager#isCapable(int)} will return false for + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} + * @see #setUceSettingEnabled(boolean) + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isUceSettingEnabled() throws ImsException { + // TODO: add SubscriptionController column for this property. + throw new UnsupportedOperationException("isUceSettingEnabled is not supported."); + } + /** + * Change the user’s setting for whether or not UCE is enabled for the associated subscription. + * @param isEnabled the user's setting for whether or not they wish for Presence and User + * Capability Exchange to be enabled. If false, + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} capability will be + * disabled, depending on which type of UCE the carrier supports. + * @see #isUceSettingEnabled() + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setUceSettingEnabled(boolean isEnabled) throws ImsException { + // TODO: add SubscriptionController column for this property. + throw new UnsupportedOperationException("setUceSettingEnabled is not supported."); + } +} diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl index 4433c1c03c1f..53e459697958 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl @@ -17,6 +17,8 @@ package android.telephony.ims.aidl; +import android.os.PersistableBundle; + import android.telephony.ims.aidl.IImsConfigCallback; import com.android.ims.ImsConfigListener; @@ -37,4 +39,5 @@ interface IImsConfig { int setConfigInt(int item, int value); // Return result code defined in ImsConfig#OperationStatusConstants int setConfigString(int item, String value); + void updateImsCarrierConfigs(in PersistableBundle bundle); } diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 74af6bf6fe6e..8f8989909f9f 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -34,7 +34,9 @@ import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.WeakHashMap; @@ -106,6 +108,16 @@ public abstract class ImsFeature { public static final int FEATURE_MAX = 3; /** + * Used for logging purposes. + * @hide + */ + public static final Map<Integer, String> FEATURE_LOG_MAP = new HashMap<Integer, String>() {{ + put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL"); + put(FEATURE_MMTEL, "MMTEL"); + put(FEATURE_RCS, "RCS"); + }}; + + /** * Integer values defining IMS features that are supported in ImsFeature. * @hide */ @@ -132,19 +144,34 @@ public abstract class ImsFeature { public @interface ImsState {} /** - * This {@link ImsFeature}'s state is unavailable and should not be communicated with. + * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will + * remove all bindings back to the framework. Any attempt to communicate with the framework + * during this time will result in an {@link IllegalStateException}. */ public static final int STATE_UNAVAILABLE = 0; /** - * This {@link ImsFeature} state is initializing and should not be communicated with. + * This {@link ImsFeature} state is initializing and should not be communicated with. This will + * remove all bindings back to the framework. Any attempt to communicate with the framework + * during this time will result in an {@link IllegalStateException}. */ public static final int STATE_INITIALIZING = 1; /** - * This {@link ImsFeature} is ready for communication. + * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods + * until {@link #onFeatureReady()} is called. */ public static final int STATE_READY = 2; /** + * Used for logging purposes. + * @hide + */ + public static final Map<Integer, String> STATE_LOG_MAP = new HashMap<Integer, String>() {{ + put(STATE_UNAVAILABLE, "UNAVAILABLE"); + put(STATE_INITIALIZING, "INITIALIZING"); + put(STATE_READY, "READY"); + }}; + + /** * Integer values defining the result codes that should be returned from * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability. * @hide @@ -208,11 +235,14 @@ public abstract class ImsFeature { /** * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask. + * <p> + * Typically this class is not used directly, but rather extended in subclasses of + * {@link ImsFeature} to provide service specific capabilities. * @hide - * @deprecated Use {@link MmTelFeature.MmTelCapabilities} instead. */ - @SystemApi // SystemApi only because it was leaked through type usage in a previous release. + @SystemApi public static class Capabilities { + /** @deprecated Use getters and accessors instead. */ protected int mCapabilities = 0; /** @@ -305,12 +335,12 @@ public abstract class ImsFeature { /** @hide */ protected final Object mLock = new Object(); - private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap( - new WeakHashMap<IImsFeatureStatusCallback, Boolean>()); + private final Set<IImsFeatureStatusCallback> mStatusCallbacks = + Collections.newSetFromMap(new WeakHashMap<>()); private @ImsState int mState = STATE_UNAVAILABLE; private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks - = new RemoteCallbackList<>(); + private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks = + new RemoteCallbackList<>(); private Capabilities mCapabilityStatus = new Capabilities(); /** @@ -322,6 +352,16 @@ public abstract class ImsFeature { } /** + * @return The SIM slot index associated with this ImsFeature. + * + * @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the + * subscription IDs associated with this slot. + */ + public final int getSlotIndex() { + return mSlotId; + } + + /** * @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE}, * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}. * @hide @@ -490,7 +530,9 @@ public abstract class ImsFeature { public abstract void onFeatureRemoved(); /** - * Called when the feature has been initialized and communication with the framework is set up. + * Called after this ImsFeature has been initialized and has been set to the + * {@link ImsState#STATE_READY} state. + * <p> * Any attempt by this feature to access the framework before this method is called will return * with an {@link IllegalStateException}. * The IMS provider should use this method to trigger registration for this feature on the IMS diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index a637e16d0a48..5fae3eebbfc6 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -16,8 +16,14 @@ package android.telephony.ims.feature; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.telephony.ims.aidl.IImsRcsFeature; +import android.telephony.ims.stub.RcsPresenceExchangeImplBase; +import android.telephony.ims.stub.RcsSipOptionsImplBase; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend @@ -32,18 +38,165 @@ public class RcsFeature extends ImsFeature { // Empty Default Implementation. }; + /** + * Contains the capabilities defined and supported by a {@link RcsFeature} in the + * form of a bitmask. The capabilities that are used in the RcsFeature are + * defined as: + * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE} + * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE} + * + * The enabled capabilities of this RcsFeature will be set by the framework + * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}. + * After the capabilities have been set, the RcsFeature may then perform the necessary bring up + * of the capability and notify the capability status as true using + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the + * framework that the capability is available for usage. + * @hide + */ + public static class RcsImsCapabilities extends Capabilities { + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = { + CAPABILITY_TYPE_OPTIONS_UCE, + CAPABILITY_TYPE_PRESENCE_UCE + }) + public @interface RcsImsCapabilityFlag {} - public RcsFeature() { - super(); + /** + * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the + * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS. + * If not set, this RcsFeature should not service capability requests. + * @hide + */ + public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0; + + /** + * This carrier supports User Capability Exchange using a presence server as defined by the + * framework. If set, the RcsFeature should support capability exchange using a presence + * server. If not set, this RcsFeature should not publish capabilities or service capability + * requests using presence. + * @hide + */ + public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; + + /**@hide*/ + public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) { + + } + + /**@hide*/ + @Override + public void addCapabilities(@RcsImsCapabilityFlag int capabilities) { + + } + + /**@hide*/ + @Override + public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) { + + } + + /**@hide*/ + @Override + public boolean isCapable(@RcsImsCapabilityFlag int capabilities) { + return false; + } + } + /** + * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is + * set, the {@link RcsFeature} has brought up the capability and is ready for framework + * requests. To change the status of the capabilities + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called. + * @hide + */ + @Override + public final RcsImsCapabilities queryCapabilityStatus() { + throw new UnsupportedOperationException(); } /** - * {@inheritDoc} + * Notify the framework that the capabilities status has changed. If a capability is enabled, + * this signals to the framework that the capability has been initialized and is ready. + * Call {@link #queryCapabilityStatus()} to return the current capability status. + * @hide + */ + public final void notifyCapabilitiesStatusChanged(RcsImsCapabilities c) { + throw new UnsupportedOperationException(); + } + + /** + * Provides the RcsFeature with the ability to return the framework capability configuration set + * by the framework. When the framework calls + * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to + * enable or disable capability A, this method should return the correct configuration for + * capability A afterwards (until it has changed). + * @hide + */ + public boolean queryCapabilityConfiguration( + @RcsImsCapabilities.RcsImsCapabilityFlag int capability) { + throw new UnsupportedOperationException(); + } + /** + * Called from the framework when the {@link RcsImsCapabilities} that have been configured for + * this {@link RcsFeature} has changed. + * <p> + * For each newly enabled capability flag, the corresponding capability should be brought up in + * the {@link RcsFeature} and registered on the network. For each newly disabled capability + * flag, the corresponding capability should be brought down, and deregistered. Once a new + * capability has been initialized and is ready for usage, the status of that capability should + * also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This + * will notify the framework that the capability is ready. + * <p> + * If for some reason one or more of these capabilities can not be enabled/disabled, + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should + * be called for each capability change that resulted in an error. + * @hide */ @Override public void changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c) { - // Do nothing for base implementation. + throw new UnsupportedOperationException(); + } + + /** + * Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}. + * <p> + * Will only be requested by the framework if capability exchange via SIP OPTIONS is + * configured as capable during a + * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} + * operation and the RcsFeature sets the status of the capability to true using + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. + * + * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if + * it is supported by the device. + * @hide + */ + public RcsSipOptionsImplBase getOptionsExchangeImpl() { + // Base Implementation, override to implement functionality + return new RcsSipOptionsImplBase(); + } + + /** + * Retrieve the implementation of UCE presence for this {@link RcsFeature}. + * Will only be requested by the framework if presence exchang is configured as capable during + * a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} + * operation and the RcsFeature sets the status of the capability to true using + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. + * + * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence + * exchange if it is supported by the device. + * @hide + */ + public RcsPresenceExchangeImplBase getPresenceExchangeImpl() { + // Base Implementation, override to implement functionality. + return new RcsPresenceExchangeImplBase(); + } + + /** + * Construct a new {@link RcsFeature} instance. + */ + public RcsFeature() { + super(); } /**{@inheritDoc}*/ diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index 321bfff40652..3e135cc9f048 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -19,6 +19,7 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.SystemApi; import android.content.Context; +import android.os.PersistableBundle; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.ims.aidl.IImsConfig; @@ -182,6 +183,11 @@ public class ImsConfigImplBase { return retVal; } + @Override + public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException { + getImsConfigImpl().updateImsCarrierConfigs(bundle); + } + private ImsConfigImplBase getImsConfigImpl() throws RemoteException { ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get(); if (ref == null) { @@ -342,6 +348,17 @@ public class ImsConfigImplBase { } /** + * The framework has received an RCS autoconfiguration XML file for provisioning. + * + * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format. + * @param isCompressed The XML file is compressed in gzip format and must be decompressed + * before being read. + * @hide + */ + public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { + } + + /** * Sets the configuration value for this ImsService. * * @param item an integer key. @@ -387,4 +404,11 @@ public class ImsConfigImplBase { // Base Implementation - To be overridden. return null; } + + /** + * @hide + */ + public void updateImsCarrierConfigs(PersistableBundle bundle) { + // Base Implementation - Should be overridden + } } diff --git a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java index dfb6e2cef42f..1a839fcb75b6 100644 --- a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java +++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java @@ -21,7 +21,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.feature.ImsFeature; import android.util.ArraySet; -import android.util.Pair; import java.util.Set; @@ -80,7 +79,7 @@ public final class ImsFeatureConfiguration implements Parcelable { @Override public String toString() { - return "{s=" + slotId + ", f=" + featureType + "}"; + return "{s=" + slotId + ", f=" + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "}"; } } diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java new file mode 100644 index 000000000000..289fd4c8a134 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java @@ -0,0 +1,89 @@ +/* + * 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 android.telephony.ims.stub; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Base class for different types of Capability exchange, presence using + * {@link RcsPresenceExchangeImplBase} and SIP OPTIONS exchange using {@link RcsSipOptionsImplBase}. + * + * @hide + */ +public class RcsCapabilityExchange { + + /** Service is unknown. */ + public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; + /** The command completed successfully. */ + public static final int COMMAND_CODE_SUCCESS = 1; + /** The command failed with an unknown error. */ + public static final int COMMAND_CODE_GENERIC_FAILURE = 2; + /** Invalid parameter(s). */ + public static final int COMMAND_CODE_INVALID_PARAM = 3; + /** Fetch error. */ + public static final int COMMAND_CODE_FETCH_ERROR = 4; + /** Request timed out. */ + public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; + /** Failure due to insufficient memory available. */ + public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; + /** Network connection is lost. */ + public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; + /** Requested feature/resource is not supported. */ + public static final int COMMAND_CODE_NOT_SUPPORTED = 8; + /** Contact or resource is not found. */ + public static final int COMMAND_CODE_NOT_FOUND = 9; + /** Service is not available. */ + public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; + /** No Change in Capabilities */ + public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; + + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "COMMAND_CODE_", value = { + COMMAND_CODE_SERVICE_UNKNOWN, + COMMAND_CODE_SUCCESS, + COMMAND_CODE_GENERIC_FAILURE, + COMMAND_CODE_INVALID_PARAM, + COMMAND_CODE_FETCH_ERROR, + COMMAND_CODE_REQUEST_TIMEOUT, + COMMAND_CODE_INSUFFICIENT_MEMORY, + COMMAND_CODE_LOST_NETWORK_CONNECTION, + COMMAND_CODE_NOT_SUPPORTED, + COMMAND_CODE_NOT_FOUND, + COMMAND_CODE_SERVICE_UNAVAILABLE, + COMMAND_CODE_NO_CHANGE_IN_CAP + }) + public @interface CommandCode {} + + /** + * Provides the framework with an update as to whether or not a command completed successfully + * locally. This includes capabilities requests and updates from the network. If it does not + * complete successfully, then the framework may retry the command again later, depending on the + * error. If the command does complete successfully, the framework will then wait for network + * updates. + * + * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further + * updates will be sent for this command using the associated operationToken. + * @param operationToken the token associated with the pending command. + */ + public final void onCommandUpdate(@CommandCode int code, int operationToken) { + throw new UnsupportedOperationException(); + } +} diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java new file mode 100644 index 000000000000..44024703042d --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java @@ -0,0 +1,191 @@ +/* + * 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 android.telephony.ims.stub; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.net.Uri; +import android.telephony.ims.RcsContactUceCapability; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * Base implementation for RCS User Capability Exchange using Presence. Any ImsService implementing + * this service must implement the stub methods {@link #requestCapabilities(List, int)} and + * {@link #updateCapabilities(RcsContactUceCapability, int)}. + * + * @hide + */ +public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { + + private static final String LOG_TAG = "RcsPresenceExchangeIB"; + + /** + * The request has resulted in any other 4xx/5xx/6xx that is not covered below. No retry will be + * attempted. + */ + public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; + + /** + * The request has succeeded with a “200” message from the network. + */ + public static final int RESPONSE_SUCCESS = 0; + + /** + * The request has resulted in a “403” (User Not Registered) error from the network. Will retry + * capability polling with an exponential backoff. + */ + public static final int RESPONSE_NOT_REGISTERED = 1; + + /** + * The request has resulted in a “403” (not authorized (Requestor)) error from the network. No + * retry will be attempted. + */ + public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; + + /** + * The request has resulted in a "403” (Forbidden) or other “403” error from the network and + * will be handled the same as “404” Not found. No retry will be attempted. + */ + public static final int RESPONSE_FORBIDDEN = 3; + + /** + * The request has resulted in a “404” (Not found) result from the network. No retry will be + * attempted. + */ + public static final int RESPONSE_NOT_FOUND = 4; + + /** + * The request has resulted in a “408” response. Retry after exponential backoff. + */ + public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; + + /** + * The network has responded with a “413” (Too Large) response from the network. Capability + * request contains too many items and must be shrunk before the request will be accepted. + */ + public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; + + /** + * The request has resulted in a “423” response. Retry after exponential backoff. + */ + public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; + + /** + * The request has resulted in a “503” response. Retry after exponential backoff. + */ + public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; + + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "RESPONSE_", value = { + RESPONSE_SUBSCRIBE_GENERIC_FAILURE, + RESPONSE_SUCCESS, + RESPONSE_NOT_REGISTERED, + RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE, + RESPONSE_FORBIDDEN, + RESPONSE_NOT_FOUND, + RESPONSE_SIP_REQUEST_TIMEOUT, + RESPONSE_SUBSCRIBE_TOO_LARGE, + RESPONSE_SIP_INTERVAL_TOO_SHORT, + RESPONSE_SIP_SERVICE_UNAVAILABLE + }) + public @interface PresenceResponseCode {} + + /** + * Provide the framework with a subsequent network response update to + * {@link #updateCapabilities(RcsContactUceCapability, int)} and + * {@link #requestCapabilities(List, int)} operations. + * @param code The SIP response code sent from the network for the operation token specified. + * @param reason The optional reason response from the network. If the network provided no + * reason with the code, the string should be empty. + * @param operationToken The token associated with the operation this service is providing a + * response for. + */ + public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason, + int operationToken) { + throw new UnsupportedOperationException(); + } + + /** + * Provides the framework with the requested contacts’ capabilities requested by the framework + * using {@link #requestCapabilities(List, int)} . + */ + public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos, + int operationToken) { + throw new UnsupportedOperationException(); + } + + /** + * Trigger the framework to provide a capability update using + * {@link #updateCapabilities(RcsContactUceCapability, int)}. This is typically used when trying + * to generate an initial PUBLISH for a new subscription to the network. + * <p> + * The device will cache all presence publications after boot until this method is called once. + */ + public final void onNotifyUpdateCapabilites() { + throw new UnsupportedOperationException(); + } + + /** + * Notify the framework that the device’s capabilities have been unpublished from the network. + */ + public final void onUnpublish() { + throw new UnsupportedOperationException(); + } + + /** + * The user capabilities of one or multiple contacts have been requested. + * <p> + * This must be followed up with one call to {@link #onCommandUpdate(int, int)} with an update + * as to whether or not the command completed as well as subsequent network + * updates using {@link #onNetworkResponse(int, String, int)}. When the operation is completed, + * {@link #onCapabilityRequestResponse(List, int)} should be called with + * the presence information for the contacts specified. + * @param uris A {@link List} of the URIs that the framework is requesting the UCE capabilities + * for. + * @param operationToken The token associated with this operation. Updates to this request using + * {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and + * {@link #onCapabilityRequestResponse(List, int)} must use the same operation token + * in response. + */ + public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "requestCapabilities called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } + + /** + * The capabilities of this device have been updated and should be published + * to the network. The framework will expect one {@link #onCommandUpdate(int, int)} call to + * indicate whether or not this operation failed first as well as network response + * updates to this update using {@link #onNetworkResponse(int, String, int)}. + * @param capabilities The capabilities for this device. + * @param operationToken The token associated with this operation. Any subsequent + * {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)} + * calls regarding this update must use the same token. + */ + public void updateCapabilities(@NonNull RcsContactUceCapability capabilities, + int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "updateCapabilities called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } +} diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java new file mode 100644 index 000000000000..3343074ea52c --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java @@ -0,0 +1,169 @@ +/* + * 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 android.telephony.ims.stub; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.telephony.ims.RcsContactUceCapability; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Base implementation for RCS User Capability Exchange using SIP OPTIONS. + * + * @hide + */ +public class RcsSipOptionsImplBase extends RcsCapabilityExchange { + + private static final String LOG_TAG = "RcsSipOptionsImplBase"; + + /** + * Indicates a SIP response from the remote user other than 200, 480, 408, 404, or 604. + */ + public static final int RESPONSE_GENERIC_FAILURE = -1; + + /** + * Indicates that the remote user responded with a 200 OK response. + */ + public static final int RESPONSE_SUCCESS = 0; + + /** + * Indicates that the remote user responded with a 480 TEMPORARY UNAVAILABLE response. + */ + public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; + + /** + * Indicates that the remote user responded with a 408 REQUEST TIMEOUT response. + */ + public static final int RESPONSE_REQUEST_TIMEOUT = 2; + + /** + * Indicates that the remote user responded with a 404 NOT FOUND response. + */ + public static final int RESPONSE_NOT_FOUND = 3; + + /** + * Indicates that the remote user responded with a 604 DOES NOT EXIST ANYWHERE response. + */ + public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; + + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "RESPONSE_", value = { + RESPONSE_GENERIC_FAILURE, + RESPONSE_SUCCESS, + RESPONSE_TEMPORARILY_UNAVAILABLE, + RESPONSE_REQUEST_TIMEOUT, + RESPONSE_NOT_FOUND, + RESPONSE_DOES_NOT_EXIST_ANYWHERE + }) + public @interface SipResponseCode {} + + /** + * Send the response of a SIP OPTIONS capability exchange to the framework. If {@code code} is + * {@link #RESPONSE_SUCCESS}, info must be non-null. + * @param code The SIP response code that was sent by the network in response to the request + * sent by {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}. + * @param reason The optional SIP response reason sent by the network. If none was sent, this + * should be an empty string. + * @param info the contact's UCE capabilities associated with the capability request. + * @param operationToken The token associated with the original capability request, set by + * {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}. + */ + public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason, + @Nullable RcsContactUceCapability info, int operationToken) { + throw new UnsupportedOperationException(); + } + + /** + * Inform the framework of a query for this device's UCE capabilities. + * <p> + * The framework will respond via the + * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)} or + * {@link #respondToCapabilityRequestWithError(Uri, int, String, int)} method. + * @param contactUri The URI associated with the remote contact that is requesting capabilities. + * @param remoteInfo The remote contact's capability information. + * @param operationToken An unique operation token that you have generated that will be returned + * by the framework in + * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}. + */ + public final void onRemoteCapabilityRequest(@NonNull Uri contactUri, + @NonNull RcsContactUceCapability remoteInfo, int operationToken) { + throw new UnsupportedOperationException(); + } + + /** + * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism + * in order to receive the capabilities of the remote user in response. + * <p> + * The implementer must call + * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} to send the + * response of this query back to the framework. + * @param contactUri The URI of the remote user that we wish to get the capabilities of. + * @param capabilities The capabilities of this device to send to the remote user. + * @param operationToken A token generated by the framework that will be passed through + * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} when this + * operation has succeeded. + */ + public void sendCapabilityRequest(@NonNull Uri contactUri, + @NonNull RcsContactUceCapability capabilities, int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } + + /** + * Respond to a remote capability request from the contact specified with the capabilities of + * this device. + * <p> + * The framework will use the same token and uri as what was passed in to + * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}. + * @param contactUri The URI of the remote contact. + * @param ownCapabilities The capabilities of this device. + * @param operationToken The token generated by the framework that this service obtained when + * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called. + */ + public void respondToCapabilityRequest(@NonNull String contactUri, + @NonNull RcsContactUceCapability ownCapabilities, int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } + + /** + * Respond to a remote capability request from the contact specified with the specified error. + * <p> + * The framework will use the same token and uri as what was passed in to + * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}. + * @param contactUri A URI containing the remote contact. + * @param code The SIP response code to respond with. + * @param reason A non-null String containing the reason associated with the SIP code. + * @param operationToken The token provided by the framework when + * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called. + * + */ + public void respondToCapabilityRequestWithError(@NonNull Uri contactUri, + @SipResponseCode int code, @NonNull String reason, int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } +} diff --git a/test-base/Android.mk b/test-base/Android.mk deleted file mode 100644 index a9d30cf3131a..000000000000 --- a/test-base/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH:= $(call my-dir) - -ifeq ($(HOST_OS),linux) -# Build the legacy-performance-test-hostdex library -# ================================================= -# This contains the android.test.PerformanceTestCase class only -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java -LOCAL_MODULE := legacy-performance-test-hostdex - -include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY) -endif # HOST_OS == linux diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript index 0a1742ef3867..0a1742ef3867 100644 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs +++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript diff --git a/tests/net/Android.bp b/tests/net/Android.bp index 5260b30acdfa..502aa97bfc68 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -56,6 +56,7 @@ android_test { "androidx.test.rules", "FrameworksNetCommonTests", "frameworks-base-testutils", + "frameworks-net-integration-testutils", "framework-protos", "mockito-target-minus-junit4", "net-tests-utils", diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp new file mode 100644 index 000000000000..16a68d79596b --- /dev/null +++ b/tests/net/integration/Android.bp @@ -0,0 +1,31 @@ +// +// Copyright (C) 2019 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. +// + +// Utilities for testing framework code both in integration and unit tests. +java_library { + name: "frameworks-net-integration-testutils", + srcs: ["util/**/*.java", "util/**/*.kt"], + static_libs: [ + "androidx.annotation_annotation", + "androidx.test.rules", + "junit", + "net-tests-utils", + ], + libs: [ + "services.core", + "services.net", + ], +} diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt new file mode 100644 index 000000000000..fa2b99ce5cc6 --- /dev/null +++ b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server + +import android.net.ConnectivityManager.TYPE_BLUETOOTH +import android.net.ConnectivityManager.TYPE_ETHERNET +import android.net.ConnectivityManager.TYPE_MOBILE +import android.net.ConnectivityManager.TYPE_NONE +import android.net.ConnectivityManager.TYPE_TEST +import android.net.ConnectivityManager.TYPE_VPN +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH +import android.net.NetworkCapabilities.TRANSPORT_CELLULAR +import android.net.NetworkCapabilities.TRANSPORT_ETHERNET +import android.net.NetworkCapabilities.TRANSPORT_TEST +import android.net.NetworkCapabilities.TRANSPORT_VPN +import android.net.NetworkCapabilities.TRANSPORT_WIFI + +fun transportToLegacyType(transport: Int) = when (transport) { + TRANSPORT_BLUETOOTH -> TYPE_BLUETOOTH + TRANSPORT_CELLULAR -> TYPE_MOBILE + TRANSPORT_ETHERNET -> TYPE_ETHERNET + TRANSPORT_TEST -> TYPE_TEST + TRANSPORT_VPN -> TYPE_VPN + TRANSPORT_WIFI -> TYPE_WIFI + else -> TYPE_NONE +}
\ No newline at end of file diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java new file mode 100644 index 000000000000..1e8d83c3c6cd --- /dev/null +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; + +import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType; + +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkAgent; +import android.net.NetworkCapabilities; +import android.net.NetworkFactory; +import android.net.NetworkInfo; +import android.net.NetworkMisc; +import android.net.NetworkSpecifier; +import android.net.SocketKeepalive; +import android.net.UidRange; +import android.os.ConditionVariable; +import android.os.HandlerThread; +import android.os.Message; +import android.util.Log; + +import com.android.server.connectivity.ConnectivityConstants; +import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.TestableNetworkCallback; + +import java.util.Set; + +public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { + private final NetworkInfo mNetworkInfo; + private final NetworkCapabilities mNetworkCapabilities; + private final HandlerThread mHandlerThread; + private final Context mContext; + private final String mLogTag; + + private final ConditionVariable mDisconnected = new ConditionVariable(); + private final ConditionVariable mPreventReconnectReceived = new ConditionVariable(); + private int mScore; + private NetworkAgent mNetworkAgent; + private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED; + private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE; + private Integer mExpectedKeepaliveSlot = null; + + public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context) + throws Exception { + final int type = transportToLegacyType(transport); + final String typeName = ConnectivityManager.getNetworkTypeName(type); + mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock"); + mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities.addTransportType(transport); + switch (transport) { + case TRANSPORT_ETHERNET: + mScore = 70; + break; + case TRANSPORT_WIFI: + mScore = 60; + break; + case TRANSPORT_CELLULAR: + mScore = 50; + break; + case TRANSPORT_WIFI_AWARE: + mScore = 20; + break; + case TRANSPORT_VPN: + mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN); + mScore = ConnectivityConstants.VPN_DEFAULT_SCORE; + break; + default: + throw new UnsupportedOperationException("unimplemented network type"); + } + mContext = context; + mLogTag = "Mock-" + typeName; + mHandlerThread = new HandlerThread(mLogTag); + mHandlerThread.start(); + + mNetworkAgent = makeNetworkAgent(linkProperties); + } + + protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties) + throws Exception { + return new InstrumentedNetworkAgent(this, linkProperties); + } + + public static class InstrumentedNetworkAgent extends NetworkAgent { + private final NetworkAgentWrapper mWrapper; + + public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) { + super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag, + wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore, + new NetworkMisc(), NetworkFactory.SerialNumber.NONE); + mWrapper = wrapper; + } + + @Override + public void unwanted() { + mWrapper.mDisconnected.open(); + } + + @Override + public void startSocketKeepalive(Message msg) { + int slot = msg.arg1; + if (mWrapper.mExpectedKeepaliveSlot != null) { + assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot); + } + onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError); + } + + @Override + public void stopSocketKeepalive(Message msg) { + onSocketKeepaliveEvent(msg.arg1, mWrapper.mStopKeepaliveError); + } + + @Override + protected void preventAutomaticReconnect() { + mWrapper.mPreventReconnectReceived.open(); + } + + @Override + protected void addKeepalivePacketFilter(Message msg) { + Log.i(mWrapper.mLogTag, "Add keepalive packet filter."); + } + + @Override + protected void removeKeepalivePacketFilter(Message msg) { + Log.i(mWrapper.mLogTag, "Remove keepalive packet filter."); + } + } + + public void adjustScore(int change) { + mScore += change; + mNetworkAgent.sendNetworkScore(mScore); + } + + public int getScore() { + return mScore; + } + + public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) { + mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated); + } + + public void addCapability(int capability) { + mNetworkCapabilities.addCapability(capability); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void removeCapability(int capability) { + mNetworkCapabilities.removeCapability(capability); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void setUids(Set<UidRange> uids) { + mNetworkCapabilities.setUids(uids); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void setSignalStrength(int signalStrength) { + mNetworkCapabilities.setSignalStrength(signalStrength); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) { + mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void setNetworkCapabilities(NetworkCapabilities nc, boolean sendToConnectivityService) { + mNetworkCapabilities.set(nc); + if (sendToConnectivityService) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + } + + public void connect() { + assertNotEquals("MockNetworkAgents can only be connected once", + getNetworkInfo().getDetailedState(), NetworkInfo.DetailedState.CONNECTED); + mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + public void suspend() { + mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + public void resume() { + mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + public void disconnect() { + mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + @Override + public Network getNetwork() { + return new Network(mNetworkAgent.netId); + } + + public void expectPreventReconnectReceived(long timeoutMs) { + assertTrue(mPreventReconnectReceived.block(timeoutMs)); + } + + public void expectDisconnected(long timeoutMs) { + assertTrue(mDisconnected.block(timeoutMs)); + } + + public void sendLinkProperties(LinkProperties lp) { + mNetworkAgent.sendLinkProperties(lp); + } + + public void setStartKeepaliveEvent(int reason) { + mStartKeepaliveError = reason; + } + + public void setStopKeepaliveEvent(int reason) { + mStopKeepaliveError = reason; + } + + public void setExpectedKeepaliveSlot(Integer slot) { + mExpectedKeepaliveSlot = slot; + } + + public NetworkAgent getNetworkAgent() { + return mNetworkAgent; + } + + public NetworkInfo getNetworkInfo() { + return mNetworkInfo; + } + + public NetworkCapabilities getNetworkCapabilities() { + return mNetworkCapabilities; + } + + public void waitForIdle(long timeoutMs) { + HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs); + } +} diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt new file mode 100644 index 000000000000..eb290dc7d24a --- /dev/null +++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server + +import java.util.concurrent.atomic.AtomicInteger + +/** + * A [NetIdManager] that generates ID starting from [NetIdManager.MAX_NET_ID] and decreasing, rather + * than starting from [NetIdManager.MIN_NET_ID] and increasing. + * + * Useful for testing ConnectivityService, to minimize the risk of test ConnectivityService netIDs + * overlapping with netIDs used by the real ConnectivityService on the device. + * + * IDs may still overlap if many networks have been used on the device (so the "real" netIDs + * are close to MAX_NET_ID), but this is typically not the case when running unit tests. Also, there + * is no way to fully solve the overlap issue without altering ID allocation in non-test code, as + * the real ConnectivityService could start using netIds that have been used by the test in the + * past. + */ +class TestNetIdManager : NetIdManager() { + private val nextId = AtomicInteger(MAX_NET_ID) + override fun reserveNetId() = nextId.decrementAndGet() + override fun releaseNetId(id: Int) = Unit +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index e08cb1622a90..a028a54a5b74 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -28,8 +28,6 @@ import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; -import static android.net.ConnectivityManager.TYPE_NONE; -import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; @@ -69,6 +67,7 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.RouteInfo.RTN_UNREACHABLE; +import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType; import static com.android.testutils.ConcurrentUtilsKt.await; import static com.android.testutils.ConcurrentUtilsKt.durationOf; import static com.android.testutils.ExceptionUtils.ignoreExceptions; @@ -86,6 +85,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.anyInt; @@ -93,6 +93,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -105,6 +106,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.annotation.NonNull; +import android.app.AlarmManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -125,6 +127,7 @@ import android.net.ConnectivityManager.PacketKeepaliveCallback; import android.net.ConnectivityManager.TooManyRequestsException; import android.net.ConnectivityThread; import android.net.IDnsResolver; +import android.net.IIpConnectivityMetrics; import android.net.INetd; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; @@ -139,12 +142,8 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MatchAllNetworkSpecifier; import android.net.Network; -import android.net.NetworkAgent; import android.net.NetworkCapabilities; import android.net.NetworkFactory; -import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; -import android.net.NetworkMisc; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.NetworkStack; @@ -168,7 +167,6 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.INetworkManagementService; import android.os.Looper; -import android.os.Message; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -265,10 +263,12 @@ public class ConnectivityServiceTest { // timeout. For this, our assertions should run fast enough to leave less than // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are // supposedly fired, and the time we call expectCallback. - private final static int TEST_CALLBACK_TIMEOUT_MS = 200; + private static final int TEST_CALLBACK_TIMEOUT_MS = 200; // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to // complete before callbacks are verified. - private final static int TEST_REQUEST_TIMEOUT_MS = 150; + private static final int TEST_REQUEST_TIMEOUT_MS = 150; + + private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000; private static final String CLAT_PREFIX = "v4-"; private static final String MOBILE_IFNAME = "test_rmnet_data0"; @@ -276,15 +276,19 @@ public class ConnectivityServiceTest { private static final String[] EMPTY_STRING_ARRAY = new String[0]; private MockContext mServiceContext; - private WrappedConnectivityService mService; + private HandlerThread mCsHandlerThread; + private ConnectivityService mService; private WrappedConnectivityManager mCm; - private MockNetworkAgent mWiFiNetworkAgent; - private MockNetworkAgent mCellNetworkAgent; - private MockNetworkAgent mEthernetNetworkAgent; + private TestNetworkAgentWrapper mWiFiNetworkAgent; + private TestNetworkAgentWrapper mCellNetworkAgent; + private TestNetworkAgentWrapper mEthernetNetworkAgent; private MockVpn mMockVpn; private Context mContext; private INetworkPolicyListener mPolicyListener; + private WrappedMultinetworkPolicyTracker mPolicyTracker; + private HandlerThread mAlarmManagerThread; + @Mock IIpConnectivityMetrics mIpConnectivityMetrics; @Mock IpConnectivityMetrics.Logger mMetricsService; @Mock DefaultNetworkMetrics mDefaultNetworkMetrics; @Mock INetworkManagementService mNetworkManagementService; @@ -296,6 +300,7 @@ public class ConnectivityServiceTest { @Mock PackageManager mPackageManager; @Mock UserManager mUserManager; @Mock NotificationManager mNotificationManager; + @Mock AlarmManager mAlarmManager; private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -368,6 +373,7 @@ public class ConnectivityServiceTest { if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager; if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack; if (Context.USER_SERVICE.equals(name)) return mUserManager; + if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager; return super.getSystemService(name); } @@ -397,25 +403,20 @@ public class ConnectivityServiceTest { } } - public void waitForIdle(int timeoutMsAsInt) { - long timeoutMs = timeoutMsAsInt; - HandlerUtilsKt.waitForIdle(mService.mHandlerThread, timeoutMs); - waitForIdle(mCellNetworkAgent, timeoutMs); - waitForIdle(mWiFiNetworkAgent, timeoutMs); - waitForIdle(mEthernetNetworkAgent, timeoutMs); - HandlerUtilsKt.waitForIdle(mService.mHandlerThread, timeoutMs); - HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), timeoutMs); + private void waitForIdle() { + HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + waitForIdle(mCellNetworkAgent, TIMEOUT_MS); + waitForIdle(mWiFiNetworkAgent, TIMEOUT_MS); + waitForIdle(mEthernetNetworkAgent, TIMEOUT_MS); + HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); } - public void waitForIdle(MockNetworkAgent agent, long timeoutMs) { + private void waitForIdle(TestNetworkAgentWrapper agent, long timeoutMs) { if (agent == null) { return; } - HandlerUtilsKt.waitForIdle(agent.mHandlerThread, timeoutMs); - } - - private void waitForIdle() { - waitForIdle(TIMEOUT_MS); + agent.waitForIdle(timeoutMs); } @Test @@ -429,7 +430,7 @@ public class ConnectivityServiceTest { // Bring up a network that we can use to send messages to ConnectivityService. ConditionVariable cv = waitForConnectivityBroadcasts(1); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); waitFor(cv); Network n = mWiFiNetworkAgent.getNetwork(); @@ -449,7 +450,7 @@ public class ConnectivityServiceTest { public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception { // Bring up a network that we can use to send messages to ConnectivityService. ConditionVariable cv = waitForConnectivityBroadcasts(1); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); waitFor(cv); Network n = mWiFiNetworkAgent.getNetwork(); @@ -469,7 +470,7 @@ public class ConnectivityServiceTest { fail("expected race condition at least once in " + attempts + " attempts"); } - private class MockNetworkAgent implements TestableNetworkCallback.HasNetwork { + private class TestNetworkAgentWrapper extends NetworkAgentWrapper { private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP | NETWORK_VALIDATION_PROBE_HTTPS; @@ -480,86 +481,35 @@ public class ConnectivityServiceTest { | NETWORK_VALIDATION_RESULT_PARTIAL; private static final int VALIDATION_RESULT_INVALID = 0; - private final INetworkMonitor mNetworkMonitor; - private final NetworkInfo mNetworkInfo; - private final NetworkCapabilities mNetworkCapabilities; - private final HandlerThread mHandlerThread; - private final ConditionVariable mDisconnected = new ConditionVariable(); - private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); - private final ConditionVariable mPreventReconnectReceived = new ConditionVariable(); - private int mScore; - private NetworkAgent mNetworkAgent; - private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED; - private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE; - private Integer mExpectedKeepaliveSlot = null; - // Contains the redirectUrl from networkStatus(). Before reading, wait for - // mNetworkStatusReceived. - private String mRedirectUrl; - + private INetworkMonitor mNetworkMonitor; private INetworkMonitorCallbacks mNmCallbacks; private int mNmValidationResult = VALIDATION_RESULT_BASE; private String mNmValidationRedirectUrl = null; private boolean mNmProvNotificationRequested = false; - void setNetworkValid() { - mNmValidationResult = VALIDATION_RESULT_VALID; - mNmValidationRedirectUrl = null; - } - - void setNetworkInvalid() { - mNmValidationResult = VALIDATION_RESULT_INVALID; - mNmValidationRedirectUrl = null; - } - - void setNetworkPortal(String redirectUrl) { - setNetworkInvalid(); - mNmValidationRedirectUrl = redirectUrl; - } + private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); + // Contains the redirectUrl from networkStatus(). Before reading, wait for + // mNetworkStatusReceived. + private String mRedirectUrl; - void setNetworkPartial() { - mNmValidationResult = VALIDATION_RESULT_PARTIAL; - mNmValidationRedirectUrl = null; + TestNetworkAgentWrapper(int transport) throws Exception { + this(transport, new LinkProperties()); } - void setNetworkPartialValid() { - mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID; - mNmValidationRedirectUrl = null; - } + TestNetworkAgentWrapper(int transport, LinkProperties linkProperties) + throws Exception { + super(transport, linkProperties, mServiceContext); - MockNetworkAgent(int transport) throws Exception { - this(transport, new LinkProperties()); + // Waits for the NetworkAgent to be registered, which includes the creation of the + // NetworkMonitor. + waitForIdle(TIMEOUT_MS); } - MockNetworkAgent(int transport, LinkProperties linkProperties) throws Exception { - final int type = transportToLegacyType(transport); - final String typeName = ConnectivityManager.getNetworkTypeName(type); - mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock"); - mNetworkCapabilities = new NetworkCapabilities(); - mNetworkCapabilities.addTransportType(transport); - switch (transport) { - case TRANSPORT_ETHERNET: - mScore = 70; - break; - case TRANSPORT_WIFI: - mScore = 60; - break; - case TRANSPORT_CELLULAR: - mScore = 50; - break; - case TRANSPORT_WIFI_AWARE: - mScore = 20; - break; - case TRANSPORT_VPN: - mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN); - mScore = ConnectivityConstants.VPN_DEFAULT_SCORE; - break; - default: - throw new UnsupportedOperationException("unimplemented network type"); - } - mHandlerThread = new HandlerThread("Mock-" + typeName); - mHandlerThread.start(); - + @Override + protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties) + throws Exception { mNetworkMonitor = mock(INetworkMonitor.class); + final Answer validateAnswer = inv -> { new Thread(ignoreExceptions(this::onValidationRequested)).start(); return null; @@ -576,56 +526,20 @@ public class ConnectivityServiceTest { any() /* name */, nmCbCaptor.capture()); - mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext, - "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities, - linkProperties, mScore, new NetworkMisc(), NetworkFactory.SerialNumber.NONE) { - @Override - public void unwanted() { mDisconnected.open(); } - - @Override - public void startSocketKeepalive(Message msg) { - int slot = msg.arg1; - if (mExpectedKeepaliveSlot != null) { - assertEquals((int) mExpectedKeepaliveSlot, slot); - } - onSocketKeepaliveEvent(slot, mStartKeepaliveError); - } - - @Override - public void stopSocketKeepalive(Message msg) { - onSocketKeepaliveEvent(msg.arg1, mStopKeepaliveError); - } - + final InstrumentedNetworkAgent na = new InstrumentedNetworkAgent(this, linkProperties) { @Override public void networkStatus(int status, String redirectUrl) { mRedirectUrl = redirectUrl; mNetworkStatusReceived.open(); } - - @Override - protected void preventAutomaticReconnect() { - mPreventReconnectReceived.open(); - } - - @Override - protected void addKeepalivePacketFilter(Message msg) { - Log.i(TAG, "Add keepalive packet filter."); - } - - @Override - protected void removeKeepalivePacketFilter(Message msg) { - Log.i(TAG, "Remove keepalive packet filter."); - } }; - assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId); + assertEquals(na.netId, nmNetworkCaptor.getValue().netId); mNmCallbacks = nmCbCaptor.getValue(); mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor); - // Waits for the NetworkAgent to be registered, which includes the creation of the - // NetworkMonitor. - waitForIdle(); + return na; } private void onValidationRequested() throws Exception { @@ -645,55 +559,11 @@ public class ConnectivityServiceTest { } } - public void adjustScore(int change) { - mScore += change; - mNetworkAgent.sendNetworkScore(mScore); - } - - public int getScore() { - return mScore; - } - - public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) { - mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated); - } - - public void addCapability(int capability) { - mNetworkCapabilities.addCapability(capability); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void removeCapability(int capability) { - mNetworkCapabilities.removeCapability(capability); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void setUids(Set<UidRange> uids) { - mNetworkCapabilities.setUids(uids); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void setSignalStrength(int signalStrength) { - mNetworkCapabilities.setSignalStrength(signalStrength); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) { - mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void setNetworkCapabilities(NetworkCapabilities nc, - boolean sendToConnectivityService) { - mNetworkCapabilities.set(nc); - if (sendToConnectivityService) { - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - } - + /** + * Connect without adding any internet capability. + */ public void connectWithoutInternet() { - mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - mNetworkAgent.sendNetworkInfo(mNetworkInfo); + super.connect(); } /** @@ -710,23 +580,21 @@ public class ConnectivityServiceTest { * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET. */ public void connect(boolean validated, boolean hasInternet) { - assertEquals("MockNetworkAgents can only be connected once", - mNetworkInfo.getDetailedState(), DetailedState.IDLE); - assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)); + assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET)); - NetworkCallback callback = null; + ConnectivityManager.NetworkCallback callback = null; final ConditionVariable validatedCv = new ConditionVariable(); if (validated) { setNetworkValid(); NetworkRequest request = new NetworkRequest.Builder() - .addTransportType(mNetworkCapabilities.getTransportTypes()[0]) + .addTransportType(getNetworkCapabilities().getTransportTypes()[0]) .clearCapabilities() .build(); - callback = new NetworkCallback() { + callback = new ConnectivityManager.NetworkCallback() { public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { if (network.equals(getNetwork()) && - networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { + networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { validatedCv.open(); } } @@ -763,47 +631,29 @@ public class ConnectivityServiceTest { connect(false); } - public void suspend() { - mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null); - mNetworkAgent.sendNetworkInfo(mNetworkInfo); - } - - public void resume() { - mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - mNetworkAgent.sendNetworkInfo(mNetworkInfo); - } - - public void disconnect() { - mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); - mNetworkAgent.sendNetworkInfo(mNetworkInfo); - } - - public Network getNetwork() { - return new Network(mNetworkAgent.netId); - } - - public ConditionVariable getPreventReconnectReceived() { - return mPreventReconnectReceived; - } - - public ConditionVariable getDisconnectedCV() { - return mDisconnected; + void setNetworkValid() { + mNmValidationResult = VALIDATION_RESULT_VALID; + mNmValidationRedirectUrl = null; } - public void sendLinkProperties(LinkProperties lp) { - mNetworkAgent.sendLinkProperties(lp); + void setNetworkInvalid() { + mNmValidationResult = VALIDATION_RESULT_INVALID; + mNmValidationRedirectUrl = null; } - public void setStartKeepaliveError(int error) { - mStartKeepaliveError = error; + void setNetworkPortal(String redirectUrl) { + setNetworkInvalid(); + mNmValidationRedirectUrl = redirectUrl; } - public void setStopKeepaliveError(int error) { - mStopKeepaliveError = error; + void setNetworkPartial() { + mNmValidationResult = VALIDATION_RESULT_PARTIAL; + mNmValidationRedirectUrl = null; } - public void setExpectedKeepaliveSlot(Integer slot) { - mExpectedKeepaliveSlot = slot; + void setNetworkPartialValid() { + mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID; + mNmValidationRedirectUrl = null; } public String waitForRedirectUrl() { @@ -811,12 +661,12 @@ public class ConnectivityServiceTest { return mRedirectUrl; } - public NetworkAgent getNetworkAgent() { - return mNetworkAgent; + public void expectDisconnected() { + expectDisconnected(TIMEOUT_MS); } - public NetworkCapabilities getNetworkCapabilities() { - return mNetworkCapabilities; + public void expectPreventReconnectReceived() { + expectPreventReconnectReceived(TIMEOUT_MS); } } @@ -995,15 +845,15 @@ public class ConnectivityServiceTest { private boolean mConnected = false; // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does // not inherit from NetworkAgent. - private MockNetworkAgent mMockNetworkAgent; + private TestNetworkAgentWrapper mMockNetworkAgent; public MockVpn(int userId) { super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService, userId); } - public void setNetworkAgent(MockNetworkAgent agent) { - waitForIdle(agent, TIMEOUT_MS); + public void setNetworkAgent(TestNetworkAgentWrapper agent) { + agent.waitForIdle(TIMEOUT_MS); mMockNetworkAgent = agent; mNetworkAgent = agent.getNetworkAgent(); mNetworkCapabilities.set(agent.getNetworkCapabilities()); @@ -1070,187 +920,45 @@ public class ConnectivityServiceTest { } } - private class FakeWakeupMessage extends WakeupMessage { - private static final int UNREASONABLY_LONG_WAIT = 1000; - - public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd) { - super(context, handler, cmdName, cmd); - } - - public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd, - int arg1, int arg2, Object obj) { - super(context, handler, cmdName, cmd, arg1, arg2, obj); + private void mockVpn(int uid) { + synchronized (mService.mVpns) { + int userId = UserHandle.getUserId(uid); + mMockVpn = new MockVpn(userId); + // This has no effect unless the VPN is actually connected, because things like + // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN + // netId, and check if that network is actually connected. + mService.mVpns.put(userId, mMockVpn); } + } - @Override - public void schedule(long when) { - long delayMs = when - SystemClock.elapsedRealtime(); - if (delayMs < 0) delayMs = 0; - if (delayMs > UNREASONABLY_LONG_WAIT) { - fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT + - "ms into the future: " + delayMs); - } - Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj); - mHandler.sendMessageDelayed(msg, delayMs); - } + private void setUidRulesChanged(int uidRules) throws RemoteException { + mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules); + } - @Override - public void cancel() { - mHandler.removeMessages(mCmd, mObj); - } + private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException { + mPolicyListener.onRestrictBackgroundChanged(restrictBackground); + } - @Override - public void onAlarm() { - throw new AssertionError("Should never happen. Update this fake."); - } + private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) { + return mService.getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd; } - private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker { - public volatile boolean configRestrictsAvoidBadWifi; - public volatile int configMeteredMultipathPreference; + private static class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker { + volatile boolean mConfigRestrictsAvoidBadWifi; + volatile int mConfigMeteredMultipathPreference; - public WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) { + WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) { super(c, h, r); } @Override public boolean configRestrictsAvoidBadWifi() { - return configRestrictsAvoidBadWifi; + return mConfigRestrictsAvoidBadWifi; } @Override public int configMeteredMultipathPreference() { - return configMeteredMultipathPreference; - } - } - - private class WrappedConnectivityService extends ConnectivityService { - public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker; - private MockableSystemProperties mSystemProperties; - - public WrappedConnectivityService(Context context, INetworkManagementService netManager, - INetworkStatsService statsService, INetworkPolicyManager policyManager, - IpConnectivityLog log, INetd netd, IDnsResolver dnsResolver) { - super(context, netManager, statsService, policyManager, dnsResolver, log, netd); - mNetd = netd; - mLingerDelayMs = TEST_LINGER_DELAY_MS; - } - - @Override - protected MockableSystemProperties getSystemProperties() { - // Minimal approach to overriding system properties: let most calls fall through to real - // device values, and only override ones values that are important to this test. - mSystemProperties = spy(new MockableSystemProperties()); - when(mSystemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0); - when(mSystemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false); - return mSystemProperties; - } - - @Override - protected Tethering makeTethering() { - return mock(Tethering.class); - } - - @Override - protected ProxyTracker makeProxyTracker() { - return mock(ProxyTracker.class); - } - - @Override - protected int reserveNetId() { - while (true) { - final int netId = super.reserveNetId(); - - // Don't overlap test NetIDs with real NetIDs as binding sockets to real networks - // can have odd side-effects, like network validations succeeding. - Context context = InstrumentationRegistry.getContext(); - final Network[] networks = ConnectivityManager.from(context).getAllNetworks(); - boolean overlaps = false; - for (Network network : networks) { - if (netId == network.netId) { - overlaps = true; - break; - } - } - if (overlaps) continue; - - return netId; - } - } - - @Override - protected boolean queryUserAccess(int uid, int netId) { - return true; - } - - public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) { - return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd; - } - - @Override - public MultinetworkPolicyTracker createMultinetworkPolicyTracker( - Context c, Handler h, Runnable r) { - final WrappedMultinetworkPolicyTracker tracker = new WrappedMultinetworkPolicyTracker(c, h, r); - return tracker; - } - - public WrappedMultinetworkPolicyTracker getMultinetworkPolicyTracker() { - return (WrappedMultinetworkPolicyTracker) mMultinetworkPolicyTracker; - } - - @Override - protected NetworkStackClient getNetworkStack() { - return mNetworkStack; - } - - @Override - public WakeupMessage makeWakeupMessage( - Context context, Handler handler, String cmdName, int cmd, Object obj) { - return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj); - } - - @Override - public boolean hasService(String name) { - // Currenty, the only relevant service that ConnectivityService checks for is - // ETHERNET_SERVICE. - return Context.ETHERNET_SERVICE.equals(name); - } - - @Override - protected IpConnectivityMetrics.Logger metricsLogger() { - return mMetricsService; - } - - @Override - protected void registerNetdEventCallback() { - } - - public void mockVpn(int uid) { - synchronized (mVpns) { - int userId = UserHandle.getUserId(uid); - mMockVpn = new MockVpn(userId); - // This has no effect unless the VPN is actually connected, because things like - // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN - // netId, and check if that network is actually connected. - mVpns.put(userId, mMockVpn); - } - } - - public void waitForIdle(int timeoutMs) { - HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs); - } - - public void waitForIdle() { - waitForIdle(TIMEOUT_MS); - } - - public void setUidRulesChanged(int uidRules) throws RemoteException { - mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules); - } - - public void setRestrictBackgroundChanged(boolean restrictBackground) - throws RemoteException { - mPolicyListener.onRestrictBackgroundChanged(restrictBackground); + return mConfigMeteredMultipathPreference; } } @@ -1296,13 +1004,22 @@ public class ConnectivityServiceTest { LocalServices.addService( NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class)); - mService = new WrappedConnectivityService(mServiceContext, + mAlarmManagerThread = new HandlerThread("TestAlarmManager"); + mAlarmManagerThread.start(); + initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler()); + + mCsHandlerThread = new HandlerThread("TestConnectivityService"); + final ConnectivityService.Dependencies deps = makeDependencies(); + mService = new ConnectivityService(mServiceContext, mNetworkManagementService, mStatsService, mNpm, + mMockDnsResolver, mock(IpConnectivityLog.class), mMockNetd, - mMockDnsResolver); + deps); + mService.mLingerDelayMs = TEST_LINGER_DELAY_MS; + verify(deps).makeMultinetworkPolicyTracker(any(), any(), any()); final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor = ArgumentCaptor.forClass(INetworkPolicyListener.class); @@ -1313,7 +1030,7 @@ public class ConnectivityServiceTest { // getSystemService() correctly. mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService); mService.systemReady(); - mService.mockVpn(Process.myUid()); + mockVpn(Process.myUid()); mCm.bindProcessToNetwork(null); // Ensure that the default setting for Captive Portals is used for most tests @@ -1322,6 +1039,57 @@ public class ConnectivityServiceTest { setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); } + private ConnectivityService.Dependencies makeDependencies() { + final MockableSystemProperties systemProperties = spy(new MockableSystemProperties()); + when(systemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0); + when(systemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false); + + final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class); + doReturn(mCsHandlerThread).when(deps).makeHandlerThread(); + doReturn(new TestNetIdManager()).when(deps).makeNetIdManager(); + doReturn(mNetworkStack).when(deps).getNetworkStack(); + doReturn(systemProperties).when(deps).getSystemProperties(); + doReturn(mock(Tethering.class)).when(deps).makeTethering(any(), any(), any(), any(), any()); + doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); + doReturn(mMetricsService).when(deps).getMetricsLogger(); + doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt()); + doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics(); + doReturn(true).when(deps).hasService(Context.ETHERNET_SERVICE); + doAnswer(inv -> { + mPolicyTracker = new WrappedMultinetworkPolicyTracker( + inv.getArgument(0), inv.getArgument(1), inv.getArgument(2)); + return mPolicyTracker; + }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any()); + + return deps; + } + + private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) { + doAnswer(inv -> { + final long when = inv.getArgument(1); + final WakeupMessage wakeupMsg = inv.getArgument(3); + final Handler handler = inv.getArgument(4); + + long delayMs = when - SystemClock.elapsedRealtime(); + if (delayMs < 0) delayMs = 0; + if (delayMs > UNREASONABLY_LONG_ALARM_WAIT_MS) { + fail("Attempting to send msg more than " + UNREASONABLY_LONG_ALARM_WAIT_MS + + "ms into the future: " + delayMs); + } + alarmHandler.postDelayed(() -> handler.post(wakeupMsg::onAlarm), wakeupMsg /* token */, + delayMs); + + return null; + }).when(am).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(), + any(WakeupMessage.class), any()); + + doAnswer(inv -> { + final WakeupMessage wakeupMsg = inv.getArgument(0); + alarmHandler.removeCallbacksAndMessages(wakeupMsg /* token */); + return null; + }).when(am).cancel(any(WakeupMessage.class)); + } + @After public void tearDown() throws Exception { setAlwaysOnNetworks(false); @@ -1338,6 +1106,9 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent = null; } FakeSettingsProvider.clearSettingsProvider(); + + mCsHandlerThread.quitSafely(); + mAlarmManagerThread.quitSafely(); } private void mockDefaultPackages() throws Exception { @@ -1357,21 +1128,6 @@ public class ConnectivityServiceTest { })); } - private static int transportToLegacyType(int transport) { - switch (transport) { - case TRANSPORT_ETHERNET: - return TYPE_ETHERNET; - case TRANSPORT_WIFI: - return TYPE_WIFI; - case TRANSPORT_CELLULAR: - return TYPE_MOBILE; - case TRANSPORT_VPN: - return TYPE_VPN; - default: - return TYPE_NONE; - } - } - private void verifyActiveNetwork(int transport) { // Test getActiveNetworkInfo() assertNotNull(mCm.getActiveNetworkInfo()); @@ -1449,8 +1205,8 @@ public class ConnectivityServiceTest { @Test public void testLingering() throws Exception { verifyNoNetwork(); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); assertNull(mCm.getActiveNetworkInfo()); assertNull(mCm.getActiveNetwork()); // Test bringing up validated cellular. @@ -1474,7 +1230,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) || mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork())); // Test cellular linger timeout. - waitFor(mCellNetworkAgent.getDisconnectedCV()); + mCellNetworkAgent.expectDisconnected(); waitForIdle(); assertLength(1, mCm.getAllNetworks()); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1490,13 +1246,13 @@ public class ConnectivityServiceTest { @Test public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception { // Test bringing up unvalidated WiFi - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); ConditionVariable cv = waitForConnectivityBroadcasts(1); mWiFiNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up unvalidated cellular - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); waitForIdle(); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1505,7 +1261,7 @@ public class ConnectivityServiceTest { waitForIdle(); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); cv = waitForConnectivityBroadcasts(2); mCellNetworkAgent.connect(true); waitFor(cv); @@ -1525,13 +1281,13 @@ public class ConnectivityServiceTest { @Test public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception { // Test bringing up unvalidated cellular. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up unvalidated WiFi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.connect(false); waitFor(cv); @@ -1551,7 +1307,7 @@ public class ConnectivityServiceTest { @Test public void testUnlingeringDoesNotValidate() throws Exception { // Test bringing up unvalidated WiFi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); ConditionVariable cv = waitForConnectivityBroadcasts(1); mWiFiNetworkAgent.connect(false); waitFor(cv); @@ -1559,7 +1315,7 @@ public class ConnectivityServiceTest { assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test bringing up validated cellular. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); cv = waitForConnectivityBroadcasts(2); mCellNetworkAgent.connect(true); waitFor(cv); @@ -1579,13 +1335,13 @@ public class ConnectivityServiceTest { @Test public void testCellularOutscoresWeakWifi() throws Exception { // Test bringing up validated cellular. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up validated WiFi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.connect(true); waitFor(cv); @@ -1606,45 +1362,41 @@ public class ConnectivityServiceTest { public void testReapingNetwork() throws Exception { // Test bringing up WiFi without NET_CAPABILITY_INTERNET. // Expect it to be torn down immediately because it satisfies no requests. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); - ConditionVariable cv = mWiFiNetworkAgent.getDisconnectedCV(); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connectWithoutInternet(); - waitFor(cv); + mWiFiNetworkAgent.expectDisconnected(); // Test bringing up cellular without NET_CAPABILITY_INTERNET. // Expect it to be torn down immediately because it satisfies no requests. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); - cv = mCellNetworkAgent.getDisconnectedCV(); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mCellNetworkAgent.connectWithoutInternet(); - waitFor(cv); + mCellNetworkAgent.expectDisconnected(); // Test bringing up validated WiFi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); - cv = waitForConnectivityBroadcasts(1); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + final ConditionVariable cv = waitForConnectivityBroadcasts(1); mWiFiNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up unvalidated cellular. // Expect it to be torn down because it could never be the highest scoring network // satisfying the default request even if it validated. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); - cv = mCellNetworkAgent.getDisconnectedCV(); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); - waitFor(cv); + mCellNetworkAgent.expectDisconnected(); verifyActiveNetwork(TRANSPORT_WIFI); - cv = mWiFiNetworkAgent.getDisconnectedCV(); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + mWiFiNetworkAgent.expectDisconnected(); } @Test public void testCellularFallback() throws Exception { // Test bringing up validated cellular. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up validated WiFi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.connect(true); waitFor(cv); @@ -1676,13 +1428,13 @@ public class ConnectivityServiceTest { @Test public void testWiFiFallback() throws Exception { // Test bringing up unvalidated WiFi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); ConditionVariable cv = waitForConnectivityBroadcasts(1); mWiFiNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); cv = waitForConnectivityBroadcasts(2); mCellNetworkAgent.connect(true); waitFor(cv); @@ -1760,7 +1512,7 @@ public class ConnectivityServiceTest { // Test unvalidated networks ConditionVariable cv = waitForConnectivityBroadcasts(1); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); @@ -1775,7 +1527,7 @@ public class ConnectivityServiceTest { assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); cv = waitForConnectivityBroadcasts(2); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -1799,7 +1551,7 @@ public class ConnectivityServiceTest { assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); // Test validated networks - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); @@ -1812,7 +1564,7 @@ public class ConnectivityServiceTest { assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); genericNetworkCallback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent); @@ -1850,9 +1602,9 @@ public class ConnectivityServiceTest { TestNetworkCallback defaultCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultCallback); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); - mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); @@ -1891,7 +1643,7 @@ public class ConnectivityServiceTest { assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); for (int i = 0; i < 4; i++) { - MockNetworkAgent oldNetwork, newNetwork; + TestNetworkAgentWrapper oldNetwork, newNetwork; if (i % 2 == 0) { mWiFiNetworkAgent.adjustScore(-15); oldNetwork = mWiFiNetworkAgent; @@ -1943,7 +1695,7 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(request, callback); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); // Score: 10 callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); @@ -1952,7 +1704,7 @@ public class ConnectivityServiceTest { // Bring up wifi with a score of 20. // Cell stays up because it would satisfy the default request if it validated. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); // Score: 20 callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -1968,7 +1720,7 @@ public class ConnectivityServiceTest { // Bring up wifi with a score of 70. // Cell is lingered because it would not satisfy any request, even if it validated. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.adjustScore(50); mWiFiNetworkAgent.connect(false); // Score: 70 callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -1987,7 +1739,7 @@ public class ConnectivityServiceTest { // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but // it's arguably correct to linger it, since it was the default network before it validated. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); // TODO: Investigate sending validated before losing. @@ -2008,12 +1760,12 @@ public class ConnectivityServiceTest { assertEquals(null, mCm.getActiveNetwork()); // If a network is lingering, and we add and remove a request from it, resume lingering. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2044,7 +1796,7 @@ public class ConnectivityServiceTest { mCm.requestNetwork(cellRequest, noopCallback); // Now connect wifi, and expect it to become the default network. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); @@ -2065,7 +1817,7 @@ public class ConnectivityServiceTest { TestNetworkCallback trackDefaultCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(trackDefaultCallback); trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); - mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET); + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); mEthernetNetworkAgent.connect(true); callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent); callback.expectCallback(CallbackRecord.LOSING, mWiFiNetworkAgent); @@ -2100,8 +1852,8 @@ public class ConnectivityServiceTest { TestNetworkCallback defaultCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultCallback); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mCellNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); @@ -2142,12 +1894,12 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(request, callback); // Bring up validated cell. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); // Bring up unvalidated wifi with explicitlySelected=true. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.explicitlySelected(true, false); mWiFiNetworkAgent.connect(false); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2170,7 +1922,7 @@ public class ConnectivityServiceTest { // Disconnect wifi, and then reconnect, again with explicitlySelected=true. mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.explicitlySelected(true, false); mWiFiNetworkAgent.connect(false); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2181,7 +1933,7 @@ public class ConnectivityServiceTest { callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent); // Reconnect, again with explicitlySelected=true, but this time validate. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.explicitlySelected(true, false); mWiFiNetworkAgent.connect(true); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2191,7 +1943,7 @@ public class ConnectivityServiceTest { // BUG: the network will no longer linger, even though it's validated and outscored. // TODO: fix this. - mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET); + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); mEthernetNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); @@ -2202,7 +1954,7 @@ public class ConnectivityServiceTest { // wifi immediately. mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.explicitlySelected(true, true); mWiFiNetworkAgent.connect(false); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2215,7 +1967,7 @@ public class ConnectivityServiceTest { // Check that the network is not scored specially and that the device prefers cell data. mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.explicitlySelected(false, true); mWiFiNetworkAgent.connect(false); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2277,7 +2029,7 @@ public class ConnectivityServiceTest { assertTrue(testFactory.getMyStartRequested()); // Now bring in a higher scored network. - MockNetworkAgent testAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + TestNetworkAgentWrapper testAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); // Rather than create a validated network which complicates things by registering it's // own NetworkRequest during startup, just bump up the score to cancel out the // unvalidated penalty. @@ -2371,18 +2123,17 @@ public class ConnectivityServiceTest { @Test public void testMMSonWiFi() throws Exception { // Test bringing up cellular without MMS NetworkRequest gets reaped - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); - ConditionVariable cv = mCellNetworkAgent.getDisconnectedCV(); mCellNetworkAgent.connectWithoutInternet(); - waitFor(cv); + mCellNetworkAgent.expectDisconnected(); waitForIdle(); assertEmpty(mCm.getAllNetworks()); verifyNoNetwork(); // Test bringing up validated WiFi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); - cv = waitForConnectivityBroadcasts(1); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + final ConditionVariable cv = waitForConnectivityBroadcasts(1); mWiFiNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -2394,23 +2145,22 @@ public class ConnectivityServiceTest { mCm.requestNetwork(builder.build(), networkCallback); // Test bringing up unvalidated cellular with MMS - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); mCellNetworkAgent.connectWithoutInternet(); networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); verifyActiveNetwork(TRANSPORT_WIFI); // Test releasing NetworkRequest disconnects cellular with MMS - cv = mCellNetworkAgent.getDisconnectedCV(); mCm.unregisterNetworkCallback(networkCallback); - waitFor(cv); + mCellNetworkAgent.expectDisconnected(); verifyActiveNetwork(TRANSPORT_WIFI); } @Test public void testMMSonCell() throws Exception { // Test bringing up cellular without MMS - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent.connect(false); waitFor(cv); @@ -2423,16 +2173,16 @@ public class ConnectivityServiceTest { mCm.requestNetwork(builder.build(), networkCallback); // Test bringing up MMS cellular network - MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + TestNetworkAgentWrapper + mmsNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS); mmsNetworkAgent.connectWithoutInternet(); networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent - cv = mmsNetworkAgent.getDisconnectedCV(); mCm.unregisterNetworkCallback(networkCallback); - waitFor(cv); + mmsNetworkAgent.expectDisconnected(); verifyActiveNetwork(TRANSPORT_CELLULAR); } @@ -2446,12 +2196,12 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(request, callback); // Bring up validated mobile data. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); // Bring up wifi with partial connectivity. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connectWithPartialConnectivity(); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); @@ -2484,7 +2234,7 @@ public class ConnectivityServiceTest { // Disconnect and reconnect wifi with partial connectivity again. mWiFiNetworkAgent.disconnect(); callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connectWithPartialConnectivity(); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); @@ -2500,7 +2250,7 @@ public class ConnectivityServiceTest { // If user accepted partial connectivity before, and device reconnects to that network // again, but now the network has full connectivity. The network shouldn't contain // NET_CAPABILITY_PARTIAL_CONNECTIVITY. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); // acceptUnvalidated is also used as setting for accepting partial networks. mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */, true /* acceptUnvalidated */); @@ -2524,7 +2274,7 @@ public class ConnectivityServiceTest { // The user accepted partial connectivity and selected "don't ask again". Now the user // reconnects to the partial connectivity network. Switch to wifi as soon as partial // connectivity is detected. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */, true /* acceptUnvalidated */); mWiFiNetworkAgent.connectWithPartialConnectivity(); @@ -2548,7 +2298,7 @@ public class ConnectivityServiceTest { // If the user accepted partial connectivity, and the device auto-reconnects to the partial // connectivity network, it should contain both PARTIAL_CONNECTIVITY and VALIDATED. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.explicitlySelected(false /* explicitlySelected */, true /* acceptUnvalidated */); @@ -2580,7 +2330,7 @@ public class ConnectivityServiceTest { // Bring up a network with a captive portal. // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); String redirectUrl = "http://android.com/path"; mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl); captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2629,7 +2379,7 @@ public class ConnectivityServiceTest { // Bring up a network with a captive portal. // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); String firstRedirectUrl = "http://example.com/firstPath"; mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2642,7 +2392,7 @@ public class ConnectivityServiceTest { // Bring up a network with a captive portal. // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); String secondRedirectUrl = "http://example.com/secondPath"; mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl); captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2680,7 +2430,7 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(validatedRequest, validatedCallback); // Bring up wifi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); @@ -2740,14 +2490,12 @@ public class ConnectivityServiceTest { setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID); // Bring up a network with a captive portal. // Expect it to fail to connect and not result in any callbacks. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); String firstRedirectUrl = "http://example.com/firstPath"; - ConditionVariable disconnectCv = mWiFiNetworkAgent.getDisconnectedCV(); - ConditionVariable avoidCv = mWiFiNetworkAgent.getPreventReconnectReceived(); mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl); - waitFor(disconnectCv); - waitFor(avoidCv); + mWiFiNetworkAgent.expectDisconnected(); + mWiFiNetworkAgent.expectPreventReconnectReceived(); assertNoCallbacks(captivePortalCallback, validatedCallback); } @@ -2846,7 +2594,7 @@ public class ConnectivityServiceTest { LocalStringNetworkSpecifier nsFoo = new LocalStringNetworkSpecifier("foo"); LocalStringNetworkSpecifier nsBar = new LocalStringNetworkSpecifier("bar"); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -2973,7 +2721,7 @@ public class ConnectivityServiceTest { public void writeToParcel(Parcel dest, int flags) {} } - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier(); @@ -3026,14 +2774,14 @@ public class ConnectivityServiceTest { cellNetworkCallback.assertNoCallback(); // Bring up cell and expect CALLBACK_AVAILABLE. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring up wifi and expect CALLBACK_AVAILABLE. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); cellNetworkCallback.assertNoCallback(); defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); @@ -3046,7 +2794,7 @@ public class ConnectivityServiceTest { assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring up cell. Expect no default network callback, since it won't be the default. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultNetworkCallback.assertNoCallback(); @@ -3065,7 +2813,8 @@ public class ConnectivityServiceTest { assertEquals(null, mCm.getActiveNetwork()); final int uid = Process.myUid(); - final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); @@ -3090,7 +2839,7 @@ public class ConnectivityServiceTest { mCm.requestNetwork(cellRequest, cellNetworkCallback); // Bring up the mobile network. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); // We should get onAvailable(), onCapabilitiesChanged(), and @@ -3160,7 +2909,7 @@ public class ConnectivityServiceTest { waitForIdle(); } - private boolean isForegroundNetwork(MockNetworkAgent network) { + private boolean isForegroundNetwork(TestNetworkAgentWrapper network) { NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork()); assertNotNull(nc); return nc.hasCapability(NET_CAPABILITY_FOREGROUND); @@ -3179,13 +2928,13 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(request, callback); mCm.registerNetworkCallback(fgRequest, fgCallback); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); assertTrue(isForegroundNetwork(mCellNetworkAgent)); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); // When wifi connects, cell lingers. @@ -3278,7 +3027,7 @@ public class ConnectivityServiceTest { } }); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); // Don't request that the network validate, because otherwise connect() will block until // the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired, // and we won't actually measure anything. @@ -3295,7 +3044,7 @@ public class ConnectivityServiceTest { onAvailableDispatchingDuration <= CONNECT_TIME_LIMIT_MS); // Give wifi a high enough score that we'll linger cell when wifi comes up. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.adjustScore(40); mWiFiNetworkAgent.connect(false); @@ -3338,7 +3087,7 @@ public class ConnectivityServiceTest { assertTrue(testFactory.getMyStartRequested()); // Bring up wifi. The factory stops looking for a network. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); // Score 60 - 40 penalty for not validated yet, then 60 when it validates testFactory.expectAddRequestsWithScores(20, 60); mWiFiNetworkAgent.connect(true); @@ -3355,7 +3104,7 @@ public class ConnectivityServiceTest { // Bring up cell data and check that the factory stops looking. assertLength(1, mCm.getAllNetworks()); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); @@ -3384,48 +3133,46 @@ public class ConnectivityServiceTest { @Test public void testAvoidBadWifiSetting() throws Exception { final ContentResolver cr = mServiceContext.getContentResolver(); - final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker(); final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI; - tracker.configRestrictsAvoidBadWifi = false; + mPolicyTracker.mConfigRestrictsAvoidBadWifi = false; String[] values = new String[] {null, "0", "1"}; for (int i = 0; i < values.length; i++) { Settings.Global.putInt(cr, settingName, 1); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); waitForIdle(); String msg = String.format("config=false, setting=%s", values[i]); assertTrue(mService.avoidBadWifi()); - assertFalse(msg, tracker.shouldNotifyWifiUnvalidated()); + assertFalse(msg, mPolicyTracker.shouldNotifyWifiUnvalidated()); } - tracker.configRestrictsAvoidBadWifi = true; + mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; Settings.Global.putInt(cr, settingName, 0); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); waitForIdle(); assertFalse(mService.avoidBadWifi()); - assertFalse(tracker.shouldNotifyWifiUnvalidated()); + assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated()); Settings.Global.putInt(cr, settingName, 1); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); waitForIdle(); assertTrue(mService.avoidBadWifi()); - assertFalse(tracker.shouldNotifyWifiUnvalidated()); + assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated()); Settings.Global.putString(cr, settingName, null); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); waitForIdle(); assertFalse(mService.avoidBadWifi()); - assertTrue(tracker.shouldNotifyWifiUnvalidated()); + assertTrue(mPolicyTracker.shouldNotifyWifiUnvalidated()); } @Test public void testAvoidBadWifi() throws Exception { final ContentResolver cr = mServiceContext.getContentResolver(); - final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker(); // Pretend we're on a carrier that restricts switching away from bad wifi. - tracker.configRestrictsAvoidBadWifi = true; + mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; // File a request for cell to ensure it doesn't go down. final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); @@ -3444,17 +3191,17 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback); Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); // Bring up validated cell. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); Network cellNetwork = mCellNetworkAgent.getNetwork(); // Bring up validated wifi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); @@ -3476,14 +3223,14 @@ public class ConnectivityServiceTest { // Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect // that we switch back to cell. - tracker.configRestrictsAvoidBadWifi = false; - tracker.reevaluate(); + mPolicyTracker.mConfigRestrictsAvoidBadWifi = false; + mPolicyTracker.reevaluate(); defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); assertEquals(mCm.getActiveNetwork(), cellNetwork); // Switch back to a restrictive carrier. - tracker.configRestrictsAvoidBadWifi = true; - tracker.reevaluate(); + mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; + mPolicyTracker.reevaluate(); defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mCm.getActiveNetwork(), wifiNetwork); @@ -3498,7 +3245,7 @@ public class ConnectivityServiceTest { // Disconnect and reconnect wifi to clear the one-time switch above. mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); @@ -3512,7 +3259,7 @@ public class ConnectivityServiceTest { // Simulate the user selecting "switch" and checking the don't ask again checkbox. Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); // We now switch to cell. defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); @@ -3525,11 +3272,11 @@ public class ConnectivityServiceTest { // Simulate the user turning the cellular fallback setting off and then on. // We switch to wifi and then to cell. Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mCm.getActiveNetwork(), wifiNetwork); Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); assertEquals(mCm.getActiveNetwork(), cellNetwork); @@ -3547,14 +3294,13 @@ public class ConnectivityServiceTest { @Test public void testMeteredMultipathPreferenceSetting() throws Exception { final ContentResolver cr = mServiceContext.getContentResolver(); - final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker(); final String settingName = Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE; for (int config : Arrays.asList(0, 3, 2)) { for (String setting: Arrays.asList(null, "0", "2", "1")) { - tracker.configMeteredMultipathPreference = config; + mPolicyTracker.mConfigMeteredMultipathPreference = config; Settings.Global.putString(cr, settingName, setting); - tracker.reevaluate(); + mPolicyTracker.reevaluate(); waitForIdle(); final int expected = (setting != null) ? Integer.parseInt(setting) : config; @@ -3575,7 +3321,7 @@ public class ConnectivityServiceTest { final TestNetworkCallback networkCallback = new TestNetworkCallback(); mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false, TEST_CALLBACK_TIMEOUT_MS); @@ -3595,7 +3341,7 @@ public class ConnectivityServiceTest { final TestNetworkCallback networkCallback = new TestNetworkCallback(); mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false, TEST_CALLBACK_TIMEOUT_MS); @@ -3623,7 +3369,7 @@ public class ConnectivityServiceTest { networkCallback.expectCallback(CallbackRecord.UNAVAILABLE, null); // create a network satisfying request - validate that request not triggered - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); networkCallback.assertNoCallback(); } @@ -3646,7 +3392,7 @@ public class ConnectivityServiceTest { networkCallback.assertNoCallback(); // create a network satisfying request - validate that request not triggered - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); networkCallback.assertNoCallback(); } @@ -3876,7 +3622,7 @@ public class ConnectivityServiceTest { assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork())); } - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); ConditionVariable cv = waitForConnectivityBroadcasts(1); mWiFiNetworkAgent.connect(true); waitFor(cv); @@ -3940,10 +3686,10 @@ public class ConnectivityServiceTest { callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED); // Check that a started keepalive can be stopped. - mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS); ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); callback.expectStarted(); - mWiFiNetworkAgent.setStopKeepaliveError(PacketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStopKeepaliveEvent(PacketKeepalive.SUCCESS); ka.stop(); callback.expectStopped(); @@ -3961,7 +3707,7 @@ public class ConnectivityServiceTest { ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); callback.expectStarted(); mWiFiNetworkAgent.disconnect(); - waitFor(mWiFiNetworkAgent.getDisconnectedCV()); + mWiFiNetworkAgent.expectDisconnected(); callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK); // ... and that stopping it after that has no adverse effects. @@ -3972,7 +3718,7 @@ public class ConnectivityServiceTest { // Reconnect. myNet = connectKeepaliveNetwork(lp); - mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS); // Check that keepalive slots start from 1 and increment. The first one gets slot 1. mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); @@ -4092,12 +3838,12 @@ public class ConnectivityServiceTest { } // Check that a started keepalive can be stopped. - mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); try (SocketKeepalive ka = mCm.createSocketKeepalive( myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { ka.start(validKaInterval); callback.expectStarted(); - mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS); ka.stop(); callback.expectStopped(); @@ -4137,7 +3883,7 @@ public class ConnectivityServiceTest { ka.start(validKaInterval); callback.expectStarted(); mWiFiNetworkAgent.disconnect(); - waitFor(mWiFiNetworkAgent.getDisconnectedCV()); + mWiFiNetworkAgent.expectDisconnected(); callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK); // ... and that stopping it after that has no adverse effects. @@ -4150,7 +3896,7 @@ public class ConnectivityServiceTest { // Reconnect. myNet = connectKeepaliveNetwork(lp); - mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); // Check that keepalive slots start from 1 and increment. The first one gets slot 1. mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); @@ -4187,7 +3933,7 @@ public class ConnectivityServiceTest { // assertFalse(isUdpPortInUse(srcPort2)); mWiFiNetworkAgent.disconnect(); - waitFor(mWiFiNetworkAgent.getDisconnectedCV()); + mWiFiNetworkAgent.expectDisconnected(); mWiFiNetworkAgent = null; } @@ -4263,7 +4009,7 @@ public class ConnectivityServiceTest { testSocketV6.close(); mWiFiNetworkAgent.disconnect(); - waitFor(mWiFiNetworkAgent.getDisconnectedCV()); + mWiFiNetworkAgent.expectDisconnected(); mWiFiNetworkAgent = null; } @@ -4279,8 +4025,8 @@ public class ConnectivityServiceTest { lp.addLinkAddress(new LinkAddress(myIPv4, 25)); lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); Network myNet = connectKeepaliveNetwork(lp); - mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS); - mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS); TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor); @@ -4316,7 +4062,7 @@ public class ConnectivityServiceTest { // assertFalse(isUdpPortInUse(srcPort)); mWiFiNetworkAgent.disconnect(); - waitFor(mWiFiNetworkAgent.getDisconnectedCV()); + mWiFiNetworkAgent.expectDisconnected(); mWiFiNetworkAgent = null; } @@ -4379,9 +4125,9 @@ public class ConnectivityServiceTest { TestNetworkPinner.pin(mServiceContext, wifiRequest); assertNull(mCm.getBoundNetworkForProcess()); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); // When wi-fi connects, expect to be pinned. @@ -4394,7 +4140,7 @@ public class ConnectivityServiceTest { assertNotPinnedToWifi(); // Reconnecting does not cause the pin to come back. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); assertFalse(TestNetworkPinner.awaitPin(100)); assertNotPinnedToWifi(); @@ -4416,14 +4162,14 @@ public class ConnectivityServiceTest { // Pinning takes effect even if the pinned network is the default when the pin is set... TestNetworkPinner.pin(mServiceContext, wifiRequest); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); assertTrue(TestNetworkPinner.awaitPin(100)); assertPinnedToWifiWithWifiDefault(); // ... and is maintained even when that network is no longer the default. cv = waitForConnectivityBroadcasts(1); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mCellNetworkAgent.connect(true); waitFor(cv); assertPinnedToWifiWithCellDefault(); @@ -4526,7 +4272,7 @@ public class ConnectivityServiceTest { ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1); verifyNoNetwork(); - MockNetworkAgent wifiAware = new MockNetworkAgent(TRANSPORT_WIFI_AWARE); + TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE); assertNull(mCm.getActiveNetworkInfo()); Network[] allNetworks = mCm.getAllNetworks(); @@ -4599,7 +4345,7 @@ public class ConnectivityServiceTest { // Verify direct routes are added when network agent is first registered in // ConnectivityService. - MockNetworkAgent networkAgent = new MockNetworkAgent(TRANSPORT_WIFI, lp); + TestNetworkAgentWrapper networkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); networkAgent.connect(true); networkCallback.expectCallback(CallbackRecord.AVAILABLE, networkAgent); networkCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, networkAgent); @@ -4631,8 +4377,8 @@ public class ConnectivityServiceTest { @Test public void testStatsIfacesChanged() throws Exception { - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()}; Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()}; @@ -4708,7 +4454,7 @@ public class ConnectivityServiceTest { // Clear any interactions that occur as a result of CS starting up. reset(mMockDnsResolver); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); waitForIdle(); verify(mMockDnsResolver, never()).setResolverConfiguration(any()); verifyNoMoreInteractions(mMockDnsResolver); @@ -4791,7 +4537,7 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_CELLULAR).build(); mCm.requestNetwork(cellRequest, cellNetworkCallback); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); waitForIdle(); // CS tells netd about the empty DNS config for this network. verify(mMockDnsResolver, never()).setResolverConfiguration(any()); @@ -4880,7 +4626,7 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_CELLULAR).build(); mCm.requestNetwork(cellRequest, cellNetworkCallback); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); waitForIdle(); LinkProperties lp = new LinkProperties(); mCellNetworkAgent.sendLinkProperties(lp); @@ -5015,7 +4761,7 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(defaultCallback); defaultCallback.assertNoCallback(); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -5025,14 +4771,16 @@ public class ConnectivityServiceTest { vpnNetworkCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.setUids(ranges); // VPN networks do not satisfy the default request and are automatically validated // by NetworkMonitor - assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities)); + assertFalse(NetworkMonitorUtils.isValidationRequired( + vpnNetworkAgent.getNetworkCapabilities())); vpnNetworkAgent.setNetworkValid(); vpnNetworkAgent.connect(false); @@ -5110,13 +4858,14 @@ public class ConnectivityServiceTest { final TestNetworkCallback defaultCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultCallback); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); @@ -5140,13 +4889,14 @@ public class ConnectivityServiceTest { final TestNetworkCallback defaultCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultCallback); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); @@ -5170,14 +4920,15 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(callback); // Bring up Ethernet. - mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET); + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); mEthernetNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); callback.assertNoCallback(); // Bring up a VPN that has the INTERNET capability, initially unvalidated. final int uid = Process.myUid(); - final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); @@ -5199,9 +4950,10 @@ public class ConnectivityServiceTest { assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED)); assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET)); - assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities)); + assertFalse(NetworkMonitorUtils.isValidationRequired( + vpnNetworkAgent.getNetworkCapabilities())); assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired( - vpnNetworkAgent.mNetworkCapabilities)); + vpnNetworkAgent.getNetworkCapabilities())); // Pretend that the VPN network validates. vpnNetworkAgent.setNetworkValid(); @@ -5229,7 +4981,8 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); vpnNetworkCallback.assertNoCallback(); - final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); @@ -5246,7 +4999,7 @@ public class ConnectivityServiceTest { assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); // Connect cell and use it as an underlying network. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); mService.setUnderlyingNetworksForVpn( @@ -5257,7 +5010,7 @@ public class ConnectivityServiceTest { && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); @@ -5327,7 +5080,8 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); vpnNetworkCallback.assertNoCallback(); - final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); @@ -5345,7 +5099,7 @@ public class ConnectivityServiceTest { assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); // Connect to Cell; Cell is the default network. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, @@ -5354,7 +5108,7 @@ public class ConnectivityServiceTest { && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); // Connect to WiFi; WiFi is the new default. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); @@ -5382,7 +5136,7 @@ public class ConnectivityServiceTest { public void testIsActiveNetworkMeteredOverWifi() throws Exception { // Returns true by default when no network is available. assertTrue(mCm.isActiveNetworkMetered()); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); waitForIdle(); @@ -5394,7 +5148,7 @@ public class ConnectivityServiceTest { public void testIsActiveNetworkMeteredOverCell() throws Exception { // Returns true by default when no network is available. assertTrue(mCm.isActiveNetworkMetered()); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); mCellNetworkAgent.connect(true); waitForIdle(); @@ -5406,14 +5160,15 @@ public class ConnectivityServiceTest { public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() throws Exception { // Returns true by default when no network is available. assertTrue(mCm.isActiveNetworkMetered()); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); mCellNetworkAgent.connect(true); waitForIdle(); assertTrue(mCm.isActiveNetworkMetered()); // Connect VPN network. By default it is using current default network (Cell). - MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); final int uid = Process.myUid(); ranges.add(new UidRange(uid, uid)); @@ -5429,7 +5184,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // Connect WiFi. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); waitForIdle(); @@ -5460,20 +5215,21 @@ public class ConnectivityServiceTest { public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception { // Returns true by default when no network is available. assertTrue(mCm.isActiveNetworkMetered()); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); mCellNetworkAgent.connect(true); waitForIdle(); assertTrue(mCm.isActiveNetworkMetered()); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); waitForIdle(); assertFalse(mCm.isActiveNetworkMetered()); // Connect VPN network. - MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); final int uid = Process.myUid(); ranges.add(new UidRange(uid, uid)); @@ -5531,14 +5287,15 @@ public class ConnectivityServiceTest { public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() throws Exception { // Returns true by default when no network is available. assertTrue(mCm.isActiveNetworkMetered()); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); waitForIdle(); assertFalse(mCm.isActiveNetworkMetered()); // Connect VPN network. - MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); final int uid = Process.myUid(); ranges.add(new UidRange(uid, uid)); @@ -5582,21 +5339,21 @@ public class ConnectivityServiceTest { .build(); mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mService.setUidRulesChanged(RULE_REJECT_ALL); + setUidRulesChanged(RULE_REJECT_ALL); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); // ConnectivityService should cache it not to invoke the callback again. - mService.setUidRulesChanged(RULE_REJECT_METERED); + setUidRulesChanged(RULE_REJECT_METERED); cellNetworkCallback.assertNoCallback(); - mService.setUidRulesChanged(RULE_NONE); + setUidRulesChanged(RULE_NONE); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); - mService.setUidRulesChanged(RULE_REJECT_METERED); + setUidRulesChanged(RULE_REJECT_METERED); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); // Restrict the network based on UID rule and NOT_METERED capability change. @@ -5607,18 +5364,18 @@ public class ConnectivityServiceTest { cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); - mService.setUidRulesChanged(RULE_ALLOW_METERED); + setUidRulesChanged(RULE_ALLOW_METERED); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); - mService.setUidRulesChanged(RULE_NONE); + setUidRulesChanged(RULE_NONE); cellNetworkCallback.assertNoCallback(); // Restrict the network based on BackgroundRestricted. - mService.setRestrictBackgroundChanged(true); + setRestrictBackgroundChanged(true); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); - mService.setRestrictBackgroundChanged(true); + setRestrictBackgroundChanged(true); cellNetworkCallback.assertNoCallback(); - mService.setRestrictBackgroundChanged(false); + setRestrictBackgroundChanged(false); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); cellNetworkCallback.assertNoCallback(); @@ -5631,18 +5388,18 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(defaultCallback); // No Networkcallbacks invoked before any network is active. - mService.setUidRulesChanged(RULE_REJECT_ALL); - mService.setUidRulesChanged(RULE_NONE); - mService.setUidRulesChanged(RULE_REJECT_METERED); + setUidRulesChanged(RULE_REJECT_ALL); + setUidRulesChanged(RULE_NONE); + setUidRulesChanged(RULE_REJECT_METERED); defaultCallback.assertNoCallback(); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent); // Allow to use the network after switching to NOT_METERED network. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); @@ -5658,8 +5415,8 @@ public class ConnectivityServiceTest { defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); // Verify there's no Networkcallbacks invoked after data saver on/off. - mService.setRestrictBackgroundChanged(true); - mService.setRestrictBackgroundChanged(false); + setRestrictBackgroundChanged(true); + setRestrictBackgroundChanged(false); defaultCallback.assertNoCallback(); mCellNetworkAgent.disconnect(); @@ -5709,7 +5466,7 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(networkRequest, networkCallback); // Prepare ipv6 only link properties. - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); final int cellNetId = mCellNetworkAgent.getNetwork().netId; final LinkProperties cellLp = new LinkProperties(); cellLp.setInterfaceName(MOBILE_IFNAME); @@ -5756,7 +5513,7 @@ public class ConnectivityServiceTest { verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); // When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started. - Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent); + Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent); assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix()); mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */, kNat64PrefixString, 96); @@ -5863,7 +5620,7 @@ public class ConnectivityServiceTest { .build(); mCm.registerNetworkCallback(networkRequest, networkCallback); - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); final LinkProperties cellLp = new LinkProperties(); cellLp.setInterfaceName(MOBILE_IFNAME); mCellNetworkAgent.sendLinkProperties(cellLp); @@ -5873,7 +5630,7 @@ public class ConnectivityServiceTest { verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), eq(ConnectivityManager.TYPE_MOBILE)); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); final LinkProperties wifiLp = new LinkProperties(); wifiLp.setInterfaceName(WIFI_IFNAME); mWiFiNetworkAgent.sendLinkProperties(wifiLp); @@ -5898,7 +5655,7 @@ public class ConnectivityServiceTest { eq(ConnectivityManager.TYPE_MOBILE)); // reconnect wifi - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); wifiLp.setInterfaceName(WIFI_IFNAME); mWiFiNetworkAgent.sendLinkProperties(wifiLp); mWiFiNetworkAgent.connect(true); @@ -5944,7 +5701,7 @@ public class ConnectivityServiceTest { public void testTcpBufferReset() throws Exception { final String testTcpBufferSizes = "1,2,3,4,5,6"; - mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); reset(mMockNetd); // Switching default network updates TCP buffer sizes. mCellNetworkAgent.connect(false); @@ -5960,7 +5717,7 @@ public class ConnectivityServiceTest { @Test public void testGetGlobalProxyForNetwork() throws Exception { final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); final Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo); assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork)); @@ -5969,7 +5726,7 @@ public class ConnectivityServiceTest { @Test public void testGetProxyForActiveNetwork() throws Exception { final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); waitForIdle(); assertNull(mService.getProxyForNetwork(null)); @@ -5988,14 +5745,15 @@ public class ConnectivityServiceTest { final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); // Set up a WiFi network with no proxy - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); waitForIdle(); assertNull(mService.getProxyForNetwork(null)); // Set up a VPN network with a proxy final int uid = Process.myUid(); - final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); final ArraySet<UidRange> ranges = new ArraySet<>(); ranges.add(new UidRange(uid, uid)); mMockVpn.setUids(ranges); @@ -6044,7 +5802,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); + final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); // Connected VPN should have interface rules set up. There are two expected invocations, // one during VPN uid update, one during VPN LinkProperties update @@ -6070,7 +5828,8 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange); + final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn( + lp, Process.SYSTEM_UID, vpnRange); // Legacy VPN should not have interface rules set up verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -6085,7 +5844,8 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange); + final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn( + lp, Process.SYSTEM_UID, vpnRange); // IPv6 unreachable route should not be misinterpreted as a default route verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -6098,7 +5858,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); + final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); // Connected VPN should have interface rules set up. There are two expected invocations, // one during VPN uid update, one during VPN LinkProperties update @@ -6147,7 +5907,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final UidRange vpnRange = UidRange.createForUser(VPN_USER); - final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, + final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, Collections.singleton(vpnRange)); reset(mMockNetd); @@ -6169,9 +5929,10 @@ public class ConnectivityServiceTest { } - private MockNetworkAgent establishVpn(LinkProperties lp, int establishingUid, + private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid, Set<UidRange> vpnRange) throws Exception { - final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN, lp); + final TestNetworkAgentWrapper + vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp); vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.connect(); diff --git a/tools/dump-coverage/README.md b/tools/dump-coverage/README.md index 2bab4bc8c984..d1c10bc2e520 100644 --- a/tools/dump-coverage/README.md +++ b/tools/dump-coverage/README.md @@ -16,7 +16,7 @@ adb shell 'mkdir /data/data/com.android.deskclock/folder-to-use' Then we can run the command to dump the data: ``` -adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use' +adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use/coverage-file.ec' ``` We can also reset the coverage information with @@ -28,10 +28,10 @@ adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so= then perform more actions, then dump the data again. To get the files, we can get ``` -adb pull /data/data/com.android.deskclock/folder-to-use ~/path-on-your-computer +adb pull /data/data/com.android.deskclock/folder-to-use/coverage-file.ec ~/path-on-your-computer ``` -And you should have timestamped `.exec` files on your machine under the folder `~/path-on-your-computer` +And you should have `coverage-file.ec` on your machine under the folder `~/path-on-your-computer` # Details diff --git a/tools/dump-coverage/dump_coverage.cc b/tools/dump-coverage/dump_coverage.cc index 3de1865b8018..0808e776f190 100644 --- a/tools/dump-coverage/dump_coverage.cc +++ b/tools/dump-coverage/dump_coverage.cc @@ -18,20 +18,10 @@ #include <jvmti.h> #include <string.h> -#include <atomic> -#include <ctime> #include <fstream> -#include <iomanip> -#include <iostream> -#include <istream> -#include <memory> -#include <sstream> -#include <string> -#include <vector> using std::get; using std::tuple; -using std::chrono::system_clock; namespace dump_coverage { @@ -87,35 +77,11 @@ static jbyteArray GetExecutionData(JNIEnv* env) { return java_result_array; } -// Gets the filename to write execution data to -// dirname: the directory in which to place the file -// outputs <dirname>/YYYY-MM-DD-HH-MM-SS.SSS.exec -static std::string GetFilename(const std::string& dirname) { - system_clock::time_point time_point = system_clock::now(); - auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(time_point); - auto fractional_time = time_point - seconds; - auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(fractional_time); - - std::time_t time = system_clock::to_time_t(time_point); - auto tm = *std::gmtime(&time); - - std::ostringstream oss; - oss - << dirname - << "/" - << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S.") - << std::setfill('0') << std::setw(3) << millis.count() - << ".ec"; - return oss.str(); -} - -// Writes the execution data to a file -// data, length: represent the data, as a sequence of bytes -// dirname: directory name to contain the file +// Writes the execution data to a file. +// data, length: represent the data, as a sequence of bytes. +// filename: file to write coverage data to. // returns JNI_ERR if there is an error in writing the file, otherwise JNI_OK. -static jint WriteFile(const char* data, int length, const std::string& dirname) { - auto filename = GetFilename(dirname); - +static jint WriteFile(const char* data, int length, const std::string& filename) { LOG(INFO) << "Writing file of length " << length << " to '" << filename << "'"; std::ofstream file(filename, std::ios::binary); @@ -136,11 +102,11 @@ static jint WriteFile(const char* data, int length, const std::string& dirname) return JNI_OK; } -// Grabs execution data and writes it to a file -// dirname: directory name to contain the file +// Grabs execution data and writes it to a file. +// filename: file to write coverage data to. // returns JNI_ERR if there is an error writing the file. // Will crash if the Agent isn't found or if any Java Exception occurs. -static jint Dump(const std::string& dirname) { +static jint Dump(const std::string& filename) { LOG(INFO) << "Dumping file"; JNIEnv* env = GetJNIEnv(); @@ -152,12 +118,12 @@ static jint Dump(const std::string& dirname) { int result_len = env->GetArrayLength(java_result_array); - return WriteFile((const char*) result_ptr, result_len, dirname); + return WriteFile((const char*) result_ptr, result_len, filename); } // Resets execution data, performing the equivalent of // Agent.getInstance().reset(); -// args: should be empty +// args: should be empty. // returns JNI_ERR if the arguments are invalid. // Will crash if the Agent isn't found or if any Java Exception occurs. static jint Reset(const std::string& args) { diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp index 2488341bfd97..87b31d22af32 100644 --- a/tools/preload-check/Android.bp +++ b/tools/preload-check/Android.bp @@ -19,4 +19,5 @@ java_test_host { libs: ["tradefed"], test_suites: ["general-tests"], required: ["preload-check-device"], + data: [":preload-check-device"], } diff --git a/tools/preload-check/device/Android.bp b/tools/preload-check/device/Android.bp index 7782b0d378ae..f40d8ba5a287 100644 --- a/tools/preload-check/device/Android.bp +++ b/tools/preload-check/device/Android.bp @@ -20,7 +20,6 @@ java_test_helper_library { sdk_version: "current", srcs: ["src/**/*.java"], - test_suites: ["general-tests"], dex_preopt: { enabled: false, }, diff --git a/tools/preload2/Android.bp b/tools/preload2/Android.bp deleted file mode 100644 index 5809421da3e8..000000000000 --- a/tools/preload2/Android.bp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2015 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -java_library_host { - name: "preload2", - - srcs: ["src/**/*.java"], - - // To connect to devices (and take hprof dumps). - static_libs: [ - "ddmlib-prebuilt", - "tools-common-prebuilt", - - // To process hprof dumps. - "perflib-prebuilt", - - "trove-prebuilt", - "guavalib", - - // For JDWP access we use the framework in the JDWP tests from Apache Harmony, for - // convenience (and to not depend on internal JDK APIs). - "apache-harmony-jdwp-tests", - "junit", - ], - - // Copy to build artifacts - dist: { - targets: [ - "dist_files", - ], - }, -} - -// Copy the preload-tool shell script to the host's bin directory. -sh_binary_host { - name: "preload-tool", - src: "preload-tool", - required: ["preload2"], -} diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool deleted file mode 100644 index 322b62fda071..000000000000 --- a/tools/preload2/preload-tool +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (C) 2015 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script is used on the host only. It uses a common subset -# shell dialect that should work well. It is partially derived -# from art/tools/art. - -function follow_links() { - if [ z"$BASH_SOURCE" != z ]; then - file="$BASH_SOURCE" - else - file="$0" - fi - while [ -h "$file" ]; do - # On Mac OS, readlink -f doesn't work. - file="$(readlink "$file")" - done - echo "$file" -} - - -PROG_NAME="$(follow_links)" -PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" -ANDROID_ROOT=$PROG_DIR/.. - -java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@ diff --git a/tools/preload2/src/com/android/preload/ClientUtils.java b/tools/preload2/src/com/android/preload/ClientUtils.java deleted file mode 100644 index 71ef025d6587..000000000000 --- a/tools/preload2/src/com/android/preload/ClientUtils.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload; - -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; - -/** - * Helper class for common communication with a Client (the ddms name for a running application). - * - * Instances take a default timeout parameter that's applied to all functions without explicit - * timeout. Timeouts are in milliseconds. - */ -public class ClientUtils { - - private int defaultTimeout; - - public ClientUtils() { - this(10000); - } - - public ClientUtils(int defaultTimeout) { - this.defaultTimeout = defaultTimeout; - } - - /** - * Shortcut for findClient with default timeout. - */ - public Client findClient(IDevice device, String processName, int processPid) { - return findClient(device, processName, processPid, defaultTimeout); - } - - /** - * Find the client with the given process name or process id. The name takes precedence over - * the process id (if valid). Stop looking after the given timeout. - * - * @param device The device to communicate with. - * @param processName The name of the process. May be null. - * @param processPid The pid of the process. Values less than or equal to zero are ignored. - * @param timeout The amount of milliseconds to wait, at most. - * @return The client, if found. Otherwise null. - */ - public Client findClient(IDevice device, String processName, int processPid, int timeout) { - WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout); - return wfc.get(); - } - - /** - * Shortcut for findAllClients with default timeout. - */ - public Client[] findAllClients(IDevice device) { - return findAllClients(device, defaultTimeout); - } - - /** - * Retrieve all clients known to the given device. Wait at most the given timeout. - * - * @param device The device to investigate. - * @param timeout The amount of milliseconds to wait, at most. - * @return An array of clients running on the given device. May be null depending on the - * device implementation. - */ - public Client[] findAllClients(IDevice device, int timeout) { - if (device.hasClients()) { - return device.getClients(); - } - WaitForClients wfc = new WaitForClients(device, timeout); - return wfc.get(); - } - - private static class WaitForClient implements IClientChangeListener { - - private IDevice device; - private String processName; - private int processPid; - private long timeout; - private Client result; - - public WaitForClient(IDevice device, String processName, int processPid, long timeout) { - this.device = device; - this.processName = processName; - this.processPid = processPid; - this.timeout = timeout; - this.result = null; - } - - public Client get() { - synchronized (this) { - AndroidDebugBridge.addClientChangeListener(this); - - // Maybe it's already there. - if (result == null) { - result = searchForClient(device); - } - - if (result == null) { - try { - wait(timeout); - } catch (InterruptedException e) { - // Note: doesn't guard for spurious wakeup. - } - } - } - - AndroidDebugBridge.removeClientChangeListener(this); - return result; - } - - private Client searchForClient(IDevice device) { - if (processName != null) { - Client tmp = device.getClient(processName); - if (tmp != null) { - return tmp; - } - } - if (processPid > 0) { - String name = device.getClientName(processPid); - if (name != null && !name.isEmpty()) { - Client tmp = device.getClient(name); - if (tmp != null) { - return tmp; - } - } - } - if (processPid > 0) { - // Try manual search. - for (Client cl : device.getClients()) { - if (cl.getClientData().getPid() == processPid - && cl.getClientData().getClientDescription() != null) { - return cl; - } - } - } - return null; - } - - private boolean isTargetClient(Client c) { - if (processPid > 0 && c.getClientData().getPid() == processPid) { - return true; - } - if (processName != null - && processName.equals(c.getClientData().getClientDescription())) { - return true; - } - return false; - } - - @Override - public void clientChanged(Client arg0, int arg1) { - synchronized (this) { - if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) { - if (isTargetClient(arg0)) { - result = arg0; - notifyAll(); - } - } - } - } - } - - private static class WaitForClients implements IClientChangeListener { - - private IDevice device; - private long timeout; - - public WaitForClients(IDevice device, long timeout) { - this.device = device; - this.timeout = timeout; - } - - public Client[] get() { - synchronized (this) { - AndroidDebugBridge.addClientChangeListener(this); - - if (device.hasClients()) { - return device.getClients(); - } - - try { - wait(timeout); // Note: doesn't guard for spurious wakeup. - } catch (InterruptedException exc) { - } - - // We will be woken up when the first client data arrives. Sleep a little longer - // to give (hopefully all of) the rest of the clients a chance to become available. - // Note: a loop with timeout is brittle as well and complicated, just accept this - // for now. - try { - Thread.sleep(500); - } catch (InterruptedException exc) { - } - } - - AndroidDebugBridge.removeClientChangeListener(this); - - return device.getClients(); - } - - @Override - public void clientChanged(Client arg0, int arg1) { - synchronized (this) { - if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) { - notifyAll(); - } - } - } - } -} diff --git a/tools/preload2/src/com/android/preload/DeviceUtils.java b/tools/preload2/src/com/android/preload/DeviceUtils.java deleted file mode 100644 index 18cab7bee12d..000000000000 --- a/tools/preload2/src/com/android/preload/DeviceUtils.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload; - -import com.android.ddmlib.AdbCommandRejectedException; -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; -import com.android.preload.classdataretrieval.hprof.Hprof; -import com.android.ddmlib.DdmPreferences; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.IShellOutputReceiver; -import com.android.ddmlib.SyncException; -import com.android.ddmlib.TimeoutException; - -import java.io.File; -import java.io.IOException; -import java.util.Date; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -/** - * Helper class for some device routines. - */ -public class DeviceUtils { - - // Locations - private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes"; - // Shell commands - private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE; - private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art"; - private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE; - private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE; - private static final String START_SHELL_CMD = "start"; - private static final String STOP_SHELL_CMD = "stop"; - private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system"; - private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\""; - - public static void init(int debugPort) { - DdmPreferences.setSelectedDebugPort(debugPort); - - Hprof.init(); - - AndroidDebugBridge.init(true); - - AndroidDebugBridge.createBridge(); - } - - /** - * Run a command in the shell on the device. - */ - public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) { - doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit); - } - - /** - * Run a command in the shell on the device. Collects and returns the console output. - */ - public static String doShellReturnString(IDevice device, String cmdline, long timeout, - TimeUnit unit) { - CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver(); - doShell(device, cmdline, rec, timeout, unit); - return rec.toString(); - } - - /** - * Run a command in the shell on the device, directing all output to the given receiver. - */ - public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver, - long timeout, TimeUnit unit) { - try { - device.executeShellCommand(cmdline, receiver, timeout, unit); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * Run am start on the device. - */ - public static void doAMStart(IDevice device, String name, String activity) { - doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS); - } - - /** - * Find the device with the given serial. Give up after the given timeout (in milliseconds). - */ - public static IDevice findDevice(String serial, int timeout) { - WaitForDevice wfd = new WaitForDevice(serial, timeout); - return wfd.get(); - } - - /** - * Get all devices ddms knows about. Wait at most for the given timeout. - */ - public static IDevice[] findDevices(int timeout) { - WaitForDevice wfd = new WaitForDevice(null, timeout); - wfd.get(); - return AndroidDebugBridge.getBridge().getDevices(); - } - - /** - * Return the build type of the given device. This is the value of the "ro.build.type" - * system property. - */ - public static String getBuildType(IDevice device) { - try { - Future<String> buildType = device.getSystemProperty("ro.build.type"); - return buildType.get(500, TimeUnit.MILLISECONDS); - } catch (Exception e) { - } - return null; - } - - /** - * Check whether the given device has a pre-optimized boot image. More precisely, checks - * whether /system/framework/ * /boot.art exists. - */ - public static boolean hasPrebuiltBootImage(IDevice device) { - String ret = - doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS); - - return !ret.contains("No such file or directory"); - } - - /** - * Write over the preloaded-classes file with an empty or existing file and regenerate the boot - * image as necessary. - * - * @param device - * @param pcFile - * @param bootTimeout - * @throws AdbCommandRejectedException - * @throws IOException - * @throws TimeoutException - * @throws SyncException - * @return true if successfully overwritten, false otherwise - */ - public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout) - throws AdbCommandRejectedException, IOException, TimeoutException, SyncException { - boolean writeEmpty = (pcFile == null); - if (writeEmpty) { - // Check if the preloaded-classes file is already empty. - String oldContent = - doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS); - if (oldContent.trim().equals("")) { - System.out.println("Preloaded-classes already empty."); - return true; - } - } - - // Stop the system server etc. - doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS); - // Remount the read-only system partition - doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS); - // Delete the preloaded-classes file - doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS); - // Delete the dalvik cache files - doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS); - if (writeEmpty) { - // Write an empty preloaded-classes file - doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS); - } else { - // Push the new preloaded-classes file - device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE); - } - // Manually reset the boot complete flag - doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS); - // Restart system server on the device - doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS); - // Wait for the boot complete flag and return the outcome. - return waitForBootComplete(device, bootTimeout); - } - - private static boolean waitForBootComplete(IDevice device, long timeout) { - // Do a loop checking each second whether bootcomplete. Wait for at most the given - // threshold. - Date startDate = new Date(); - for (;;) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // Ignore spurious wakeup. - } - // Check whether bootcomplete. - String ret = - doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS); - if (ret.trim().equals("1")) { - break; - } - System.out.println("Still not booted: " + ret); - - // Check whether we timed out. This is a simplistic check that doesn't take into account - // things like switches in time. - Date endDate = new Date(); - long seconds = - TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS); - if (seconds > timeout) { - return false; - } - } - - return true; - } - - /** - * Enable method-tracing on device. The system should be restarted after this. - */ - public static void enableTracing(IDevice device) { - // Disable selinux. - doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS); - - // Make the profile directory world-writable. - doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS); - - // Enable streaming method tracing with a small 1K buffer. - doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS); - doShell(device, "setprop dalvik.vm.method-trace-file " - + "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS); - doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS); - doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS); - } - - private static class NullShellOutputReceiver implements IShellOutputReceiver { - @Override - public boolean isCancelled() { - return false; - } - - @Override - public void flush() {} - - @Override - public void addOutput(byte[] arg0, int arg1, int arg2) {} - } - - private static class CollectStringShellOutputReceiver implements IShellOutputReceiver { - - private StringBuilder builder = new StringBuilder(); - - @Override - public String toString() { - String ret = builder.toString(); - // Strip trailing newlines. They are especially ugly because adb uses DOS line endings. - while (ret.endsWith("\r") || ret.endsWith("\n")) { - ret = ret.substring(0, ret.length() - 1); - } - return ret; - } - - @Override - public void addOutput(byte[] arg0, int arg1, int arg2) { - builder.append(new String(arg0, arg1, arg2)); - } - - @Override - public void flush() {} - - @Override - public boolean isCancelled() { - return false; - } - } - - private static class WaitForDevice { - - private String serial; - private long timeout; - private IDevice device; - - public WaitForDevice(String serial, long timeout) { - this.serial = serial; - this.timeout = timeout; - device = null; - } - - public IDevice get() { - if (device == null) { - WaitForDeviceListener wfdl = new WaitForDeviceListener(serial); - synchronized (wfdl) { - AndroidDebugBridge.addDeviceChangeListener(wfdl); - - // Check whether we already know about this device. - IDevice[] devices = AndroidDebugBridge.getBridge().getDevices(); - if (serial != null) { - for (IDevice d : devices) { - if (serial.equals(d.getSerialNumber())) { - // Only accept if there are clients already. Else wait for the callback informing - // us that we now have clients. - if (d.hasClients()) { - device = d; - } - - break; - } - } - } else { - if (devices.length > 0) { - device = devices[0]; - } - } - - if (device == null) { - try { - wfdl.wait(timeout); - } catch (InterruptedException e) { - // Ignore spurious wakeups. - } - device = wfdl.getDevice(); - } - - AndroidDebugBridge.removeDeviceChangeListener(wfdl); - } - } - - if (device != null) { - // Wait for clients. - WaitForClientsListener wfcl = new WaitForClientsListener(device); - synchronized (wfcl) { - AndroidDebugBridge.addDeviceChangeListener(wfcl); - - if (!device.hasClients()) { - try { - wfcl.wait(timeout); - } catch (InterruptedException e) { - // Ignore spurious wakeups. - } - } - - AndroidDebugBridge.removeDeviceChangeListener(wfcl); - } - } - - return device; - } - - private static class WaitForDeviceListener implements IDeviceChangeListener { - - private String serial; - private IDevice device; - - public WaitForDeviceListener(String serial) { - this.serial = serial; - } - - public IDevice getDevice() { - return device; - } - - @Override - public void deviceChanged(IDevice arg0, int arg1) { - // We may get a device changed instead of connected. Handle like a connection. - deviceConnected(arg0); - } - - @Override - public void deviceConnected(IDevice arg0) { - if (device != null) { - // Ignore updates. - return; - } - - if (serial == null || serial.equals(arg0.getSerialNumber())) { - device = arg0; - synchronized (this) { - notifyAll(); - } - } - } - - @Override - public void deviceDisconnected(IDevice arg0) { - // Ignore disconnects. - } - - } - - private static class WaitForClientsListener implements IDeviceChangeListener { - - private IDevice myDevice; - - public WaitForClientsListener(IDevice myDevice) { - this.myDevice = myDevice; - } - - @Override - public void deviceChanged(IDevice arg0, int arg1) { - if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) { - // Got a client list, done here. - synchronized (this) { - notifyAll(); - } - } - } - - @Override - public void deviceConnected(IDevice arg0) { - } - - @Override - public void deviceDisconnected(IDevice arg0) { - } - - } - } - -} diff --git a/tools/preload2/src/com/android/preload/DumpData.java b/tools/preload2/src/com/android/preload/DumpData.java deleted file mode 100644 index d99722416a1d..000000000000 --- a/tools/preload2/src/com/android/preload/DumpData.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload; - -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Holds the collected data for a process. - */ -public class DumpData { - /** - * Name of the package (=application). - */ - String packageName; - - /** - * A map of class name to a string for the classloader. This may be a toString equivalent, - * or just a unique ID. - */ - Map<String, String> dumpData; - - /** - * The Date when this data was captured. Mostly for display purposes. - */ - Date date; - - /** - * A cached value for the number of boot classpath classes (classloader value in dumpData is - * null). - */ - int bcpClasses; - - public DumpData(String packageName, Map<String, String> dumpData, Date date) { - this.packageName = packageName; - this.dumpData = dumpData; - this.date = date; - - countBootClassPath(); - } - - public String getPackageName() { - return packageName; - } - - public Date getDate() { - return date; - } - - public Map<String, String> getDumpData() { - return dumpData; - } - - public void countBootClassPath() { - bcpClasses = 0; - for (Map.Entry<String, String> e : dumpData.entrySet()) { - if (e.getValue() == null) { - bcpClasses++; - } - } - } - - // Return an inverted mapping. - public Map<String, Set<String>> invertData() { - Map<String, Set<String>> ret = new HashMap<>(); - for (Map.Entry<String, String> e : dumpData.entrySet()) { - if (!ret.containsKey(e.getValue())) { - ret.put(e.getValue(), new HashSet<String>()); - } - ret.get(e.getValue()).add(e.getKey()); - } - return ret; - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/DumpDataIO.java b/tools/preload2/src/com/android/preload/DumpDataIO.java deleted file mode 100644 index 28625c5531e4..000000000000 --- a/tools/preload2/src/com/android/preload/DumpDataIO.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload; - -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; - -import java.io.File; -import java.io.FileReader; -import java.text.DateFormat; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * Helper class for serialization and deserialization of a collection of DumpData objects to XML. - */ -public class DumpDataIO { - - /** - * Serialize the given collection to an XML document. Returns the produced string. - */ - public static String serialize(Collection<DumpData> data) { - // We'll do this by hand, constructing a DOM or similar is too complicated for our simple - // use case. - - StringBuilder sb = new StringBuilder(); - sb.append("<preloaded-classes-data>\n"); - - for (DumpData d : data) { - serialize(d, sb); - } - - sb.append("</preloaded-classes-data>\n"); - return sb.toString(); - } - - private static void serialize(DumpData d, StringBuilder sb) { - sb.append("<data package=\"" + d.packageName + "\" date=\"" + - DateFormat.getDateTimeInstance().format(d.date) +"\">\n"); - - for (Map.Entry<String, String> e : d.dumpData.entrySet()) { - sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n"); - } - - sb.append("</data>\n"); - } - - /** - * Load a collection of DumpData objects from the given file. - */ - public static Collection<DumpData> deserialize(File f) throws Exception { - // Use SAX parsing. Our format is very simple. Don't do any schema validation or such. - - SAXParserFactory spf = SAXParserFactory.newInstance(); - spf.setNamespaceAware(false); - SAXParser saxParser = spf.newSAXParser(); - - XMLReader xmlReader = saxParser.getXMLReader(); - DumpDataContentHandler ddch = new DumpDataContentHandler(); - xmlReader.setContentHandler(ddch); - xmlReader.parse(new InputSource(new FileReader(f))); - - return ddch.data; - } - - private static class DumpDataContentHandler extends DefaultHandler { - Collection<DumpData> data = new LinkedList<DumpData>(); - DumpData openData = null; - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) - throws SAXException { - if (qName.equals("data")) { - if (openData != null) { - throw new IllegalStateException(); - } - String pkg = attributes.getValue("package"); - String dateString = attributes.getValue("date"); - - if (pkg == null || dateString == null) { - throw new IllegalArgumentException(); - } - - try { - Date date = DateFormat.getDateTimeInstance().parse(dateString); - openData = new DumpData(pkg, new HashMap<String, String>(), date); - } catch (Exception e) { - throw new RuntimeException(e); - } - } else if (qName.equals("class")) { - if (openData == null) { - throw new IllegalStateException(); - } - String className = attributes.getValue("name"); - String classLoader = attributes.getValue("classloader"); - - if (className == null || classLoader == null) { - throw new IllegalArgumentException(); - } - - openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader); - } - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - if (qName.equals("data")) { - if (openData == null) { - throw new IllegalStateException(); - } - openData.countBootClassPath(); - - data.add(openData); - openData = null; - } - } - } -} diff --git a/tools/preload2/src/com/android/preload/DumpTableModel.java b/tools/preload2/src/com/android/preload/DumpTableModel.java deleted file mode 100644 index d97cbf0df5e5..000000000000 --- a/tools/preload2/src/com/android/preload/DumpTableModel.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload; - -import java.util.ArrayList; -import java.util.List; - -import javax.swing.table.AbstractTableModel; - -/** - * A table model for collected DumpData. This is both the internal storage as well as the model - * for display. - */ -public class DumpTableModel extends AbstractTableModel { - - private List<DumpData> data = new ArrayList<DumpData>(); - - public void addData(DumpData d) { - data.add(d); - fireTableRowsInserted(data.size() - 1, data.size() - 1); - } - - public void clear() { - int size = data.size(); - if (size > 0) { - data.clear(); - fireTableRowsDeleted(0, size - 1); - } - } - - public List<DumpData> getData() { - return data; - } - - @Override - public int getRowCount() { - return data.size(); - } - - @Override - public int getColumnCount() { - return 4; - } - - @Override - public String getColumnName(int column) { - switch (column) { - case 0: - return "Package"; - case 1: - return "Date"; - case 2: - return "# All Classes"; - case 3: - return "# Boot Classpath Classes"; - - default: - throw new IndexOutOfBoundsException(String.valueOf(column)); - } - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - DumpData d = data.get(rowIndex); - switch (columnIndex) { - case 0: - return d.packageName; - case 1: - return d.date; - case 2: - return d.dumpData.size(); - case 3: - return d.bcpClasses; - - default: - throw new IndexOutOfBoundsException(String.valueOf(columnIndex)); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java deleted file mode 100644 index 2265e9557c4b..000000000000 --- a/tools/preload2/src/com/android/preload/Main.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.actions.ClearTableAction; -import com.android.preload.actions.ComputeThresholdAction; -import com.android.preload.actions.ComputeThresholdXAction; -import com.android.preload.actions.DeviceSpecific; -import com.android.preload.actions.ExportAction; -import com.android.preload.actions.ImportAction; -import com.android.preload.actions.ReloadListAction; -import com.android.preload.actions.RunMonkeyAction; -import com.android.preload.actions.ScanAllPackagesAction; -import com.android.preload.actions.ScanPackageAction; -import com.android.preload.actions.ShowDataAction; -import com.android.preload.actions.WritePreloadedClassesAction; -import com.android.preload.classdataretrieval.ClassDataRetriever; -import com.android.preload.classdataretrieval.hprof.Hprof; -import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever; -import com.android.preload.ui.IUI; -import com.android.preload.ui.SequenceUI; -import com.android.preload.ui.SwingUI; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; - -import javax.swing.Action; -import javax.swing.DefaultListModel; - -public class Main { - - /** - * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is - * off for now. - */ - public final static boolean ENABLE_TRACING = false; - - /** - * Ten-second timeout. - */ - public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000; - - /** - * Hprof timeout. Two minutes. - */ - public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000; - - private IDevice device; - private static ClientUtils clientUtils; - - private DumpTableModel dataTableModel; - private DefaultListModel<Client> clientListModel; - - private IUI ui; - - // Actions that need to be updated once a device is selected. - private Collection<DeviceSpecific> deviceSpecificActions; - - // Current main instance. - private static Main top; - private static boolean useJdwpClassDataRetriever = false; - - public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|" - + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|" - + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" + - - - // Threads - "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|" - + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|" - + "(.*\\$NoPreloadHolder$)"; - - public final static String SCAN_ALL_CMD = "scan-all"; - public final static String SCAN_PACKAGE_CMD = "scan"; - public final static String COMPUTE_FILE_CMD = "comp"; - public final static String EXPORT_CMD = "export"; - public final static String IMPORT_CMD = "import"; - public final static String WRITE_CMD = "write"; - - /** - * @param args - */ - public static void main(String[] args) { - Main m; - if (args.length > 0 && args[0].equals("--seq")) { - m = createSequencedMain(args); - } else { - m = new Main(new SwingUI()); - } - - top = m; - m.startUp(); - } - - public Main(IUI ui) { - this.ui = ui; - - clientListModel = new DefaultListModel<Client>(); - dataTableModel = new DumpTableModel(); - - clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS); // Client utils with 10s timeout. - - List<Action> actions = new ArrayList<Action>(); - actions.add(new ReloadListAction(clientUtils, null, clientListModel)); - actions.add(new ClearTableAction(dataTableModel)); - actions.add(new RunMonkeyAction(null, dataTableModel)); - actions.add(new ScanPackageAction(clientUtils, null, dataTableModel)); - actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel)); - actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2, - CLASS_PRELOAD_BLACKLIST)); - actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1, - null)); - actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel, - CLASS_PRELOAD_BLACKLIST)); - actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel)); - actions.add(new ShowDataAction(dataTableModel)); - actions.add(new ImportAction(dataTableModel)); - actions.add(new ExportAction(dataTableModel)); - - deviceSpecificActions = new ArrayList<DeviceSpecific>(); - for (Action a : actions) { - if (a instanceof DeviceSpecific) { - deviceSpecificActions.add((DeviceSpecific)a); - } - } - - ui.prepare(clientListModel, dataTableModel, actions); - } - - /** - * @param args - * @return - */ - private static Main createSequencedMain(String[] args) { - SequenceUI ui = new SequenceUI(); - Main main = new Main(ui); - - Iterator<String> it = Arrays.asList(args).iterator(); - it.next(); // --seq - // Setup - ui.choice("#" + it.next()); // Device. - ui.confirmNo(); // Prepare: no. - // Actions - try { - while (it.hasNext()) { - String op = it.next(); - // Operation: Scan a single package - if (SCAN_PACKAGE_CMD.equals(op)) { - System.out.println("Scanning package."); - ui.action(ScanPackageAction.class); - ui.client(it.next()); - // Operation: Scan all packages - } else if (SCAN_ALL_CMD.equals(op)) { - System.out.println("Scanning all packages."); - ui.action(ScanAllPackagesAction.class); - // Operation: Export the output to a file - } else if (EXPORT_CMD.equals(op)) { - System.out.println("Exporting data."); - ui.action(ExportAction.class); - ui.output(new File(it.next())); - // Operation: Import the input from a file or directory - } else if (IMPORT_CMD.equals(op)) { - System.out.println("Importing data."); - File file = new File(it.next()); - if (!file.exists()) { - throw new RuntimeException( - String.format("File does not exist, %s.", file.getAbsolutePath())); - } else if (file.isFile()) { - ui.action(ImportAction.class); - ui.input(file); - } else if (file.isDirectory()) { - for (File content : file.listFiles()) { - ui.action(ImportAction.class); - ui.input(content); - } - } - // Operation: Compute preloaded classes with specific threshold - } else if (COMPUTE_FILE_CMD.equals(op)) { - System.out.println("Compute preloaded classes."); - ui.action(ComputeThresholdXAction.class); - ui.input(it.next()); - ui.confirmYes(); - ui.output(new File(it.next())); - // Operation: Write preloaded classes from a specific file - } else if (WRITE_CMD.equals(op)) { - System.out.println("Writing preloaded classes."); - ui.action(WritePreloadedClassesAction.class); - ui.input(new File(it.next())); - } - } - } catch (NoSuchElementException e) { - System.out.println("Failed to parse action sequence correctly."); - throw e; - } - - return main; - } - - public static IUI getUI() { - return top.ui; - } - - public static ClassDataRetriever getClassDataRetriever() { - if (useJdwpClassDataRetriever) { - return new JDWPClassDataRetriever(); - } else { - return new Hprof(HPROF_TIMEOUT_MILLIS); - } - } - - public IDevice getDevice() { - return device; - } - - public void setDevice(IDevice device) { - this.device = device; - for (DeviceSpecific ds : deviceSpecificActions) { - ds.setDevice(device); - } - } - - public DefaultListModel<Client> getClientListModel() { - return clientListModel; - } - - static class DeviceWrapper { - IDevice device; - - public DeviceWrapper(IDevice d) { - device = d; - } - - @Override - public String toString() { - return device.getName() + " (#" + device.getSerialNumber() + ")"; - } - } - - private void startUp() { - getUI().showWaitDialog(); - initDevice(); - - // Load clients. - new ReloadListAction(clientUtils, getDevice(), clientListModel).run(); - - getUI().hideWaitDialog(); - getUI().ready(); - } - - private void initDevice() { - DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS); - - IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS); - if (devices == null || devices.length == 0) { - throw new RuntimeException("Could not find any devices..."); - } - - getUI().hideWaitDialog(); - - DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length]; - for (int i = 0; i < devices.length; i++) { - deviceWrappers[i] = new DeviceWrapper(devices[i]); - } - - DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device", - deviceWrappers); - if (ret != null) { - setDevice(ret.device); - } else { - System.exit(0); - } - - boolean prepare = Main.getUI().showConfirmDialog("Prepare device?", - "Do you want to prepare the device? This is highly recommended."); - if (prepare) { - String buildType = DeviceUtils.getBuildType(device); - if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) { - Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType - + ")"); - return; - } - if (DeviceUtils.hasPrebuiltBootImage(device)) { - Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot " - + "image!"); - return; - } - - if (ENABLE_TRACING) { - DeviceUtils.enableTracing(device); - } - - Main.getUI().showMessageDialog("The device will reboot. This will potentially take a " - + "long time. Please be patient."); - boolean success = false; - try { - success = DeviceUtils.overwritePreloaded(device, null, 15 * 60); - } catch (Exception e) { - System.err.println(e); - } finally { - if (!success) { - Main.getUI().showMessageDialog( - "Removing preloaded-classes failed unexpectedly!"); - } - } - } - } - - public static Map<String, String> findAndGetClassData(IDevice device, String packageName) - throws Exception { - Client client = clientUtils.findClient(device, packageName, -1); - if (client == null) { - throw new RuntimeException("Could not find client..."); - } - System.out.println("Found client: " + client); - - return getClassDataRetriever().getClassData(client); - } - -} diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java deleted file mode 100644 index 5787d8507230..000000000000 --- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.preload.Main; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; - -public abstract class AbstractThreadedAction extends AbstractAction implements Runnable { - - protected AbstractThreadedAction(String title) { - super(title); - } - - @Override - public void actionPerformed(ActionEvent e) { - if (Main.getUI().isSingleThreaded()) { - run(); - } else { - new Thread(this).start(); - } - } - -} diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java deleted file mode 100644 index 7906417b7a8d..000000000000 --- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.IDevice; - -import java.awt.event.ActionEvent; - -public abstract class AbstractThreadedDeviceSpecificAction extends AbstractThreadedAction - implements DeviceSpecific { - - protected IDevice device; - - protected AbstractThreadedDeviceSpecificAction(String title, IDevice device) { - super(title); - this.device = device; - } - - @Override - public void setDevice(IDevice device) { - this.device = device; - } - - @Override - public void actionPerformed(ActionEvent e) { - if (device == null) { - return; - } - super.actionPerformed(e); - } -} diff --git a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java b/tools/preload2/src/com/android/preload/actions/ClearTableAction.java deleted file mode 100644 index c0e4795b6d90..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpTableModel; - -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; - -public class ClearTableAction extends AbstractAction { - private final DumpTableModel dataTableModel; - - public ClearTableAction(DumpTableModel dataTableModel) { - super("Clear"); - this.dataTableModel = dataTableModel; - } - - @Override - public void actionPerformed(ActionEvent e) { - dataTableModel.clear(); - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java deleted file mode 100644 index 3a7f7f74d755..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.event.ActionEvent; -import java.io.File; -import java.io.PrintWriter; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.regex.Pattern; - -import javax.swing.AbstractAction; - -/** - * Compute an intersection of classes from the given data. A class is in the intersection if it - * appears in at least the number of threshold given packages. An optional blacklist can be - * used to filter classes from the intersection. - */ -public class ComputeThresholdAction extends AbstractThreadedAction { - protected int threshold; - private Pattern blacklist; - private DumpTableModel dataTableModel; - - /** - * Create an action with the given parameters. The blacklist is a regular expression - * that filters classes. - */ - public ComputeThresholdAction(String name, DumpTableModel dataTableModel, int threshold, - String blacklist) { - super(name); - this.dataTableModel = dataTableModel; - this.threshold = threshold; - if (blacklist != null) { - this.blacklist = Pattern.compile(blacklist); - } - } - - @Override - public void actionPerformed(ActionEvent e) { - List<DumpData> data = dataTableModel.getData(); - if (data.size() == 0) { - Main.getUI().showMessageDialog("No data available, please scan packages or run " - + "monkeys."); - return; - } - if (data.size() == 1) { - Main.getUI().showMessageDialog("Cannot compute list from only one data set, please " - + "scan packages or run monkeys."); - return; - } - - super.actionPerformed(e); - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - Map<String, Set<String>> uses = new HashMap<String, Set<String>>(); - for (DumpData d : dataTableModel.getData()) { - Main.getUI().updateWaitDialog("Merging " + d.getPackageName()); - updateClassUse(d.getPackageName(), uses, getBootClassPathClasses(d.getDumpData())); - } - - Main.getUI().updateWaitDialog("Computing thresholded set"); - Set<String> result = fromThreshold(uses, blacklist, threshold); - Main.getUI().hideWaitDialog(); - - boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size() - + " classes, would you like to save to disk?", "Save?"); - if (ret) { - File f = Main.getUI().showSaveDialog(); - if (f != null) { - saveSet(result, f); - } - } - } - - private Set<String> fromThreshold(Map<String, Set<String>> classUses, Pattern blacklist, - int threshold) { - TreeSet<String> ret = new TreeSet<>(); // TreeSet so it's nicely ordered by name. - - for (Map.Entry<String, Set<String>> e : classUses.entrySet()) { - if (e.getValue().size() >= threshold) { - if (blacklist == null || !blacklist.matcher(e.getKey()).matches()) { - ret.add(e.getKey()); - } - } - } - - return ret; - } - - private static void updateClassUse(String pkg, Map<String, Set<String>> classUses, - Set<String> classes) { - for (String className : classes) { - Set<String> old = classUses.get(className); - if (old == null) { - classUses.put(className, new HashSet<String>()); - } - classUses.get(className).add(pkg); - } - } - - private static Set<String> getBootClassPathClasses(Map<String, String> source) { - Set<String> ret = new HashSet<>(); - for (Map.Entry<String, String> e : source.entrySet()) { - if (e.getValue() == null) { - ret.add(e.getKey()); - } - } - return ret; - } - - private static void saveSet(Set<String> result, File f) { - try { - PrintWriter out = new PrintWriter(f); - for (String s : result) { - out.println(s); - } - out.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java deleted file mode 100644 index 3ec0a4c18db1..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -public class ComputeThresholdXAction extends ComputeThresholdAction { - - public ComputeThresholdXAction(String name, DumpTableModel dataTableModel, - String blacklist) { - super(name, dataTableModel, 1, blacklist); - } - - @Override - public void run() { - String value = Main.getUI().showInputDialog("Threshold?"); - - if (value != null) { - try { - threshold = Integer.parseInt(value); - super.run(); - } catch (Exception exc) { - } - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java b/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java deleted file mode 100644 index 35a8f26a99fe..000000000000 --- a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.IDevice; - -/** - * Marks an action as being device-specific. The user must set the device through the specified - * method if the device selection changes. - * - * Implementors must tolerate a null device (for example, with a no-op). This includes calling - * any methods before setDevice has been called. - */ -public interface DeviceSpecific { - - /** - * Set the device that should be used. Note that there is no restriction on calling other - * methods of the implementor before a setDevice call. Neither is device guaranteed to be - * non-null. - * - * @param device The device to use going forward. - */ - public void setDevice(IDevice device); -} diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java deleted file mode 100644 index 848a56826788..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ExportAction.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpDataIO; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; -import java.awt.event.ActionEvent; -import java.io.File; -import java.io.PrintWriter; - -public class ExportAction extends AbstractThreadedAction { - private File lastSaveFile; - private DumpTableModel dataTableModel; - - public ExportAction(DumpTableModel dataTableModel) { - super("Export data"); - this.dataTableModel = dataTableModel; - } - - @Override - public void actionPerformed(ActionEvent e) { - lastSaveFile = Main.getUI().showSaveDialog(); - if (lastSaveFile != null) { - super.actionPerformed(e); - } - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - String serialized = DumpDataIO.serialize(dataTableModel.getData()); - - if (serialized != null) { - try { - PrintWriter out = new PrintWriter(lastSaveFile); - out.println(serialized); - out.close(); - - Main.getUI().hideWaitDialog(); - } catch (Exception e) { - Main.getUI().hideWaitDialog(); - Main.getUI().showMessageDialog("Failed writing: " + e.getMessage()); - } - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java deleted file mode 100644 index bfeeb836fd45..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ImportAction.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpData; -import com.android.preload.DumpDataIO; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.event.ActionEvent; -import java.io.File; -import java.util.Collection; - -import javax.swing.AbstractAction; - -public class ImportAction extends AbstractThreadedAction { - private File[] lastOpenFiles; - private DumpTableModel dataTableModel; - - public ImportAction(DumpTableModel dataTableModel) { - super("Import data"); - this.dataTableModel = dataTableModel; - } - - @Override - public void actionPerformed(ActionEvent e) { - lastOpenFiles = Main.getUI().showOpenDialog(true); - if (lastOpenFiles != null) { - super.actionPerformed(e); - } - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - try { - for (File f : lastOpenFiles) { - try { - Collection<DumpData> data = DumpDataIO.deserialize(f); - - for (DumpData d : data) { - dataTableModel.addData(d); - } - } catch (Exception e) { - Main.getUI().showMessageDialog("Failed reading: " + e.getMessage()); - } - } - } finally { - Main.getUI().hideWaitDialog(); - } - - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java b/tools/preload2/src/com/android/preload/actions/ReloadListAction.java deleted file mode 100644 index 29f055700dfa..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.ClientUtils; - -import java.util.Arrays; -import java.util.Comparator; - -import javax.swing.DefaultListModel; - -public class ReloadListAction extends AbstractThreadedDeviceSpecificAction { - - private ClientUtils clientUtils; - private final DefaultListModel<Client> clientListModel; - - public ReloadListAction(ClientUtils utils, IDevice device, - DefaultListModel<Client> clientListModel) { - super("Reload", device); - this.clientUtils = utils; - this.clientListModel = clientListModel; - } - - @Override - public void run() { - Client[] clients = clientUtils.findAllClients(device); - if (clients != null) { - Arrays.sort(clients, new ClientComparator()); - } - clientListModel.removeAllElements(); - for (Client c : clients) { - clientListModel.addElement(c); - } - } - - private static class ClientComparator implements Comparator<Client> { - - @Override - public int compare(Client o1, Client o2) { - String s1 = o1.getClientData().getClientDescription(); - String s2 = o2.getClientData().getClientDescription(); - - if (s1 == null || s2 == null) { - // Not good, didn't get all data? - return (s1 == null) ? -1 : 1; - } - - return s1.compareTo(s2); - } - - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java deleted file mode 100644 index 29464fc7abdf..000000000000 --- a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.IDevice; -import com.android.preload.DeviceUtils; -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.event.ActionEvent; -import java.util.Date; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import javax.swing.AbstractAction; - -public class RunMonkeyAction extends AbstractAction implements DeviceSpecific { - - private final static String DEFAULT_MONKEY_PACKAGES = - "com.android.calendar,com.android.gallery3d"; - - private IDevice device; - private DumpTableModel dataTableModel; - - public RunMonkeyAction(IDevice device, DumpTableModel dataTableModel) { - super("Run monkey"); - this.device = device; - this.dataTableModel = dataTableModel; - } - - @Override - public void setDevice(IDevice device) { - this.device = device; - } - - @Override - public void actionPerformed(ActionEvent e) { - String packages = Main.getUI().showInputDialog("Please enter packages name to run with" - + " the monkey, or leave empty for default."); - if (packages == null) { - return; - } - if (packages.isEmpty()) { - packages = DEFAULT_MONKEY_PACKAGES; - } - Runnable r = new RunMonkeyRunnable(packages); - if (Main.getUI().isSingleThreaded()) { - r.run(); - } else { - new Thread(r).start(); - } - } - - private class RunMonkeyRunnable implements Runnable { - - private String packages; - private final static int ITERATIONS = 1000; - - public RunMonkeyRunnable(String packages) { - this.packages = packages; - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - try { - String pkgs[] = packages.split(","); - - for (String pkg : pkgs) { - Main.getUI().updateWaitDialog("Running monkey on " + pkg); - - try { - // Stop running app. - forceStop(pkg); - - // Little bit of breather here. - try { - Thread.sleep(1000); - } catch (Exception e) { - } - - DeviceUtils.doShell(device, "monkey -p " + pkg + " " + ITERATIONS, 1, - TimeUnit.MINUTES); - - Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg); - Map<String, String> data = Main.findAndGetClassData(device, pkg); - DumpData dumpData = new DumpData(pkg, data, new Date()); - dataTableModel.addData(dumpData); - } catch (Exception e) { - e.printStackTrace(); - } finally { - // Stop running app. - forceStop(pkg); - } - } - } finally { - Main.getUI().hideWaitDialog(); - } - } - - private void forceStop(String packageName) { - // Stop running app. - DeviceUtils.doShell(device, "force-stop " + packageName, 5, TimeUnit.SECONDS); - DeviceUtils.doShell(device, "kill " + packageName, 5, TimeUnit.SECONDS); - DeviceUtils.doShell(device, "kill `pid " + packageName + "`", 5, TimeUnit.SECONDS); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java b/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java deleted file mode 100644 index d74b8a3f6cfb..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.ClientUtils; -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.util.Date; -import java.util.Map; - -public class ScanAllPackagesAction extends AbstractThreadedDeviceSpecificAction { - - private ClientUtils clientUtils; - private DumpTableModel dataTableModel; - - public ScanAllPackagesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) { - super("Scan all packages", device); - this.clientUtils = utils; - this.dataTableModel = dataTableModel; - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - try { - Client[] clients = clientUtils.findAllClients(device); - for (Client c : clients) { - String pkg = c.getClientData().getClientDescription(); - Main.getUI().showWaitDialog(); - Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg); - - try { - Map<String, String> data = Main.getClassDataRetriever().getClassData(c); - DumpData dumpData = new DumpData(pkg, data, new Date()); - dataTableModel.addData(dumpData); - } catch (Exception e) { - e.printStackTrace(); - } - } - } finally { - Main.getUI().hideWaitDialog(); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java b/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java deleted file mode 100644 index 98492bd951bf..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.ClientUtils; -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.util.Date; -import java.util.Map; - -public class ScanPackageAction extends AbstractThreadedDeviceSpecificAction { - - private ClientUtils clientUtils; - private DumpTableModel dataTableModel; - - public ScanPackageAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) { - super("Scan package", device); - this.clientUtils = utils; - this.dataTableModel = dataTableModel; - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - try { - Client client = Main.getUI().getSelectedClient(); - if (client != null) { - work(client); - } else { - Client[] clients = clientUtils.findAllClients(device); - if (clients.length > 0) { - ClientWrapper[] clientWrappers = new ClientWrapper[clients.length]; - for (int i = 0; i < clientWrappers.length; i++) { - clientWrappers[i] = new ClientWrapper(clients[i]); - } - Main.getUI().hideWaitDialog(); - - ClientWrapper ret = Main.getUI().showChoiceDialog("Choose a package to scan", - "Choose package", - clientWrappers); - if (ret != null) { - work(ret.client); - } - } - } - } finally { - Main.getUI().hideWaitDialog(); - } - } - - private void work(Client c) { - String pkg = c.getClientData().getClientDescription(); - Main.getUI().showWaitDialog(); - Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg); - - try { - Map<String, String> data = Main.findAndGetClassData(device, pkg); - DumpData dumpData = new DumpData(pkg, data, new Date()); - dataTableModel.addData(dumpData); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private static class ClientWrapper { - private Client client; - - public ClientWrapper(Client c) { - client = c; - } - - @Override - public String toString() { - return client.getClientData().getClientDescription() + " (pid " - + client.getClientData().getPid() + ")"; - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java b/tools/preload2/src/com/android/preload/actions/ShowDataAction.java deleted file mode 100644 index 2bb175f60772..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.swing.AbstractAction; -import javax.swing.JFrame; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; - -public class ShowDataAction extends AbstractAction { - private DumpTableModel dataTableModel; - - public ShowDataAction(DumpTableModel dataTableModel) { - super("Show data"); - this.dataTableModel = dataTableModel; - } - - @Override - public void actionPerformed(ActionEvent e) { - // TODO(agampe): Auto-generated method stub - int selRow = Main.getUI().getSelectedDataTableRow(); - if (selRow != -1) { - DumpData data = dataTableModel.getData().get(selRow); - Map<String, Set<String>> inv = data.invertData(); - - StringBuilder builder = new StringBuilder(); - - // First bootclasspath. - add(builder, "Boot classpath:", inv.get(null)); - - // Now everything else. - for (String k : inv.keySet()) { - if (k != null) { - builder.append("==================\n\n"); - add(builder, k, inv.get(k)); - } - } - - JFrame newFrame = new JFrame(data.getPackageName() + " " + data.getDate()); - newFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); - newFrame.getContentPane().add(new JScrollPane(new JTextArea(builder.toString())), - BorderLayout.CENTER); - newFrame.setSize(800, 600); - newFrame.setLocationRelativeTo(null); - newFrame.setVisible(true); - } - } - - private void add(StringBuilder builder, String head, Set<String> set) { - builder.append(head); - builder.append('\n'); - addSet(builder, set); - builder.append('\n'); - } - - private void addSet(StringBuilder builder, Set<String> set) { - if (set == null) { - builder.append(" NONE\n"); - return; - } - List<String> sorted = new ArrayList<>(set); - Collections.sort(sorted); - for (String s : sorted) { - builder.append(s); - builder.append('\n'); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java b/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java deleted file mode 100644 index 9b97f1168df9..000000000000 --- a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.ClientUtils; -import com.android.preload.DeviceUtils; -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.event.ActionEvent; -import java.io.File; -import java.util.Date; -import java.util.Map; - -public class WritePreloadedClassesAction extends AbstractThreadedDeviceSpecificAction { - private File preloadedClassFile; - - public WritePreloadedClassesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) { - super("Write preloaded classes action", device); - } - - @Override - public void actionPerformed(ActionEvent e) { - File[] files = Main.getUI().showOpenDialog(true); - if (files != null && files.length > 0) { - preloadedClassFile = files[0]; - super.actionPerformed(e); - } - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - try { - // Write the new file with a 5-minute timeout - DeviceUtils.overwritePreloaded(device, preloadedClassFile, 5 * 60); - } catch (Exception e) { - System.err.println(e); - } finally { - Main.getUI().hideWaitDialog(); - } - } -} diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java deleted file mode 100644 index 8d797ee00128..000000000000 --- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.classdataretrieval.hprof; - -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData.IHprofDumpHandler; - -import java.util.ArrayList; -import java.util.List; - -public class GeneralHprofDumpHandler implements IHprofDumpHandler { - - private List<IHprofDumpHandler> handlers = new ArrayList<>(); - - public void addHandler(IHprofDumpHandler h) { - synchronized (handlers) { - handlers.add(h); - } - } - - public void removeHandler(IHprofDumpHandler h) { - synchronized (handlers) { - handlers.remove(h); - } - } - - private List<IHprofDumpHandler> getIterationList() { - synchronized (handlers) { - return new ArrayList<>(handlers); - } - } - - @Override - public void onEndFailure(Client arg0, String arg1) { - List<IHprofDumpHandler> iterList = getIterationList(); - for (IHprofDumpHandler h : iterList) { - h.onEndFailure(arg0, arg1); - } - } - - @Override - public void onSuccess(String arg0, Client arg1) { - List<IHprofDumpHandler> iterList = getIterationList(); - for (IHprofDumpHandler h : iterList) { - h.onSuccess(arg0, arg1); - } - } - - @Override - public void onSuccess(byte[] arg0, Client arg1) { - List<IHprofDumpHandler> iterList = getIterationList(); - for (IHprofDumpHandler h : iterList) { - h.onSuccess(arg0, arg1); - } - } - }
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java deleted file mode 100644 index 84ec8b7d0fdd..000000000000 --- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.classdataretrieval.hprof; - -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData; -import com.android.ddmlib.ClientData.IHprofDumpHandler; -import com.android.preload.classdataretrieval.ClassDataRetriever; -import com.android.preload.ui.NullProgressMonitor; -import com.android.tools.perflib.captures.MemoryMappedFileBuffer; -import com.android.tools.perflib.heap.ClassObj; -import com.android.tools.perflib.heap.Queries; -import com.android.tools.perflib.heap.Snapshot; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class Hprof implements ClassDataRetriever { - - private static GeneralHprofDumpHandler hprofHandler; - - public static void init() { - synchronized(Hprof.class) { - if (hprofHandler == null) { - ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler()); - } - } - } - - public static File doHprof(Client client, int timeout) { - GetHprof gh = new GetHprof(client, timeout); - return gh.get(); - } - - /** - * Return a map of class names to class-loader names derived from the hprof dump. - * - * @param hprofLocalFile - */ - public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception { - Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile)); - - Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null); - Map<String, String> retValue = new HashMap<String, String>(); - for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) { - for (ClassObj c : e.getValue()) { - String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString(); - String cName = c.getClassName(); - int aDepth = 0; - while (cName.endsWith("[]")) { - cName = cName.substring(0, cName.length()-2); - aDepth++; - } - String newName = transformPrimitiveClass(cName); - if (aDepth > 0) { - // Need to use kind-a descriptor syntax. If it was transformed, it is primitive. - if (newName.equals(cName)) { - newName = "L" + newName + ";"; - } - for (int i = 0; i < aDepth; i++) { - newName = "[" + newName; - } - } - retValue.put(newName, cl); - } - } - - // Free up memory. - snapshot.dispose(); - - return retValue; - } - - private static Map<String, String> primitiveMapping; - - static { - primitiveMapping = new HashMap<>(); - primitiveMapping.put("boolean", "Z"); - primitiveMapping.put("byte", "B"); - primitiveMapping.put("char", "C"); - primitiveMapping.put("double", "D"); - primitiveMapping.put("float", "F"); - primitiveMapping.put("int", "I"); - primitiveMapping.put("long", "J"); - primitiveMapping.put("short", "S"); - primitiveMapping.put("void", "V"); - } - - private static String transformPrimitiveClass(String name) { - String rep = primitiveMapping.get(name); - if (rep != null) { - return rep; - } - return name; - } - - private static class GetHprof implements IHprofDumpHandler { - - private File target; - private long timeout; - private Client client; - - public GetHprof(Client client, long timeout) { - this.client = client; - this.timeout = timeout; - } - - public File get() { - synchronized (this) { - hprofHandler.addHandler(this); - client.dumpHprof(); - if (target == null) { - try { - wait(timeout); - } catch (Exception e) { - System.out.println(e); - } - } - } - - hprofHandler.removeHandler(this); - return target; - } - - private void wakeUp() { - synchronized (this) { - notifyAll(); - } - } - - @Override - public void onEndFailure(Client arg0, String arg1) { - System.out.println("GetHprof.onEndFailure"); - if (client == arg0) { - wakeUp(); - } - } - - private static File createTargetFile() { - try { - return File.createTempFile("ddms", ".hprof"); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void onSuccess(String arg0, Client arg1) { - System.out.println("GetHprof.onSuccess"); - if (client == arg1) { - try { - target = createTargetFile(); - arg1.getDevice().getSyncService().pullFile(arg0, - target.getAbsoluteFile().toString(), new NullProgressMonitor()); - } catch (Exception e) { - if (target != null) { - target.delete(); - } - e.printStackTrace(); - target = null; - } - wakeUp(); - } - } - - @Override - public void onSuccess(byte[] arg0, Client arg1) { - System.out.println("GetHprof.onSuccess"); - if (client == arg1) { - try { - target = createTargetFile(); - BufferedOutputStream out = - new BufferedOutputStream(new FileOutputStream(target)); - out.write(arg0); - out.close(); - } catch (Exception e) { - if (target != null) { - target.delete(); - } - e.printStackTrace(); - target = null; - } - wakeUp(); - } - } - } - - private int timeout; - - public Hprof(int timeout) { - this.timeout = timeout; - } - - @Override - public Map<String, String> getClassData(Client client) { - File hprofLocalFile = Hprof.doHprof(client, timeout); - if (hprofLocalFile == null) { - throw new RuntimeException("Failed getting dump..."); - } - System.out.println("Dump file is " + hprofLocalFile); - - try { - return analyzeHprof(hprofLocalFile); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - hprofLocalFile.delete(); - } - } -} diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java deleted file mode 100644 index dbd4c89b27cf..000000000000 --- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.classdataretrieval.jdwp; - -import com.android.ddmlib.Client; -import com.android.preload.classdataretrieval.ClassDataRetriever; - -import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket; -import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands; -import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants; -import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; -import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase; -import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper; -import org.apache.harmony.jpda.tests.share.JPDALogWriter; -import org.apache.harmony.jpda.tests.share.JPDATestOptions; - -import java.util.HashMap; -import java.util.Map; - -public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever { - - private final Client client; - - public JDWPClassDataRetriever() { - this(null); - } - - public JDWPClassDataRetriever(Client client) { - this.client = client; - } - - - @Override - protected String getDebuggeeClassName() { - return "<unset>"; - } - - @Override - public Map<String, String> getClassData(Client client) { - return new JDWPClassDataRetriever(client).retrieve(); - } - - private Map<String, String> retrieve() { - if (client == null) { - throw new IllegalStateException(); - } - - settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort())); - settings.setDebuggeeSuspend("n"); - - logWriter = new JPDALogWriter(System.out, "", false); - - try { - internalSetUp(); - - return retrieveImpl(); - } catch (Exception e) { - e.printStackTrace(); - return null; - } finally { - internalTearDown(); - } - } - - private Map<String, String> retrieveImpl() { - try { - // Suspend the app. - { - CommandPacket packet = new CommandPacket( - JDWPCommands.VirtualMachineCommandSet.CommandSetID, - JDWPCommands.VirtualMachineCommandSet.SuspendCommand); - ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); - if (reply.getErrorCode() != JDWPConstants.Error.NONE) { - return null; - } - } - - // List all classes. - CommandPacket packet = new CommandPacket( - JDWPCommands.VirtualMachineCommandSet.CommandSetID, - JDWPCommands.VirtualMachineCommandSet.AllClassesCommand); - ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); - - if (reply.getErrorCode() != JDWPConstants.Error.NONE) { - return null; - } - - int classCount = reply.getNextValueAsInt(); - System.out.println("Runtime reported " + classCount + " classes."); - - Map<Long, String> classes = new HashMap<Long, String>(); - Map<Long, String> arrayClasses = new HashMap<Long, String>(); - - for (int i = 0; i < classCount; i++) { - byte refTypeTag = reply.getNextValueAsByte(); - long typeID = reply.getNextValueAsReferenceTypeID(); - String signature = reply.getNextValueAsString(); - /* int status = */ reply.getNextValueAsInt(); - - switch (refTypeTag) { - case JDWPConstants.TypeTag.CLASS: - case JDWPConstants.TypeTag.INTERFACE: - classes.put(typeID, signature); - break; - - case JDWPConstants.TypeTag.ARRAY: - arrayClasses.put(typeID, signature); - break; - } - } - - Map<String, String> result = new HashMap<String, String>(); - - // Parse all classes. - for (Map.Entry<Long, String> entry : classes.entrySet()) { - long typeID = entry.getKey(); - String signature = entry.getValue(); - - if (!checkClass(typeID, signature, result)) { - System.err.println("Issue investigating " + signature); - } - } - - // For arrays, look at the leaf component type. - for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) { - long typeID = entry.getKey(); - String signature = entry.getValue(); - - if (!checkArrayClass(typeID, signature, result)) { - System.err.println("Issue investigating " + signature); - } - } - - return result; - } finally { - // Resume the app. - { - CommandPacket packet = new CommandPacket( - JDWPCommands.VirtualMachineCommandSet.CommandSetID, - JDWPCommands.VirtualMachineCommandSet.ResumeCommand); - /* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet); - } - } - } - - private boolean checkClass(long typeID, String signature, Map<String, String> result) { - CommandPacket packet = new CommandPacket( - JDWPCommands.ReferenceTypeCommandSet.CommandSetID, - JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand); - packet.setNextValueAsReferenceTypeID(typeID); - ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); - if (reply.getErrorCode() != JDWPConstants.Error.NONE) { - return false; - } - - long classLoaderID = reply.getNextValueAsObjectID(); - - // TODO: Investigate the classloader to have a better string? - String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID); - - result.put(getClassName(signature), classLoaderString); - - return true; - } - - private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) { - // Classloaders of array classes are the same as the component class'. - CommandPacket packet = new CommandPacket( - JDWPCommands.ReferenceTypeCommandSet.CommandSetID, - JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand); - packet.setNextValueAsReferenceTypeID(typeID); - ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); - if (reply.getErrorCode() != JDWPConstants.Error.NONE) { - return false; - } - - long classLoaderID = reply.getNextValueAsObjectID(); - - // TODO: Investigate the classloader to have a better string? - String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID); - - // For array classes, we *need* the signature directly. - result.put(signature, classLoaderString); - - return true; - } - - private static String getClassName(String signature) { - String withoutLAndSemicolon = signature.substring(1, signature.length() - 1); - return withoutLAndSemicolon.replace('/', '.'); - } - - - private static JPDATestOptions createTestOptions(String address) { - JPDATestOptions options = new JPDATestOptions(); - options.setAttachConnectorKind(); - options.setTimeout(1000); - options.setWaitingTime(1000); - options.setTransportAddress(address); - return options; - } - - @Override - protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() { - return new PreloadDebugeeWrapper(settings, logWriter); - } -} diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java deleted file mode 100644 index b9df6d0aeb93..000000000000 --- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.classdataretrieval.jdwp; - -import org.apache.harmony.jpda.tests.framework.LogWriter; -import org.apache.harmony.jpda.tests.jdwp.share.JDWPManualDebuggeeWrapper; -import org.apache.harmony.jpda.tests.share.JPDATestOptions; - -import java.io.IOException; - -public class PreloadDebugeeWrapper extends JDWPManualDebuggeeWrapper { - - public PreloadDebugeeWrapper(JPDATestOptions options, LogWriter writer) { - super(options, writer); - } - - @Override - protected Process launchProcess(String cmdLine) throws IOException { - return null; - } - - @Override - protected void WaitForProcessExit(Process process) { - } - -} diff --git a/tools/preload2/src/com/android/preload/ui/IUI.java b/tools/preload2/src/com/android/preload/ui/IUI.java deleted file mode 100644 index 9371463e9a79..000000000000 --- a/tools/preload2/src/com/android/preload/ui/IUI.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.android.preload.ui; - -import com.android.ddmlib.Client; -import java.io.File; -import java.util.List; -import javax.swing.Action; -import javax.swing.ListModel; -import javax.swing.table.TableModel; - -/** - * UI abstraction for the tool. This allows a graphical mode, command line mode, - * or silent mode. - */ -public interface IUI { - - void prepare(ListModel<Client> clientListModel, TableModel dataTableModel, - List<Action> actions); - - void ready(); - - boolean isSingleThreaded(); - - Client getSelectedClient(); - - int getSelectedDataTableRow(); - - void showWaitDialog(); - - void updateWaitDialog(String s); - - void hideWaitDialog(); - - void showMessageDialog(String s); - - boolean showConfirmDialog(String title, String message); - - String showInputDialog(String message); - - <T> T showChoiceDialog(String title, String message, T[] choices); - - File showSaveDialog(); - - File[] showOpenDialog(boolean multi); - -} diff --git a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java b/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java deleted file mode 100644 index f45aad06ac6b..000000000000 --- a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.ui; - -import com.android.ddmlib.SyncService.ISyncProgressMonitor; - -public class NullProgressMonitor implements ISyncProgressMonitor { - - @Override - public void advance(int arg0) {} - - @Override - public boolean isCanceled() { - return false; - } - - @Override - public void start(int arg0) {} - - @Override - public void startSubTask(String arg0) {} - - @Override - public void stop() {} -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/ui/SequenceUI.java b/tools/preload2/src/com/android/preload/ui/SequenceUI.java deleted file mode 100644 index dc6a4f389b10..000000000000 --- a/tools/preload2/src/com/android/preload/ui/SequenceUI.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.android.preload.ui; - -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData; -import java.io.File; -import java.util.LinkedList; -import java.util.List; -import javax.swing.Action; -import javax.swing.ListModel; -import javax.swing.table.TableModel; - -public class SequenceUI implements IUI { - - private ListModel<Client> clientListModel; - @SuppressWarnings("unused") - private TableModel dataTableModel; - private List<Action> actions; - - private List<Object> sequence = new LinkedList<>(); - - public SequenceUI() { - } - - @Override - public boolean isSingleThreaded() { - return true; - } - - @Override - public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel, - List<Action> actions) { - this.clientListModel = clientListModel; - this.dataTableModel = dataTableModel; - this.actions = actions; - } - - public SequenceUI action(Action a) { - sequence.add(a); - return this; - } - - public SequenceUI action(Class<? extends Action> actionClass) { - for (Action a : actions) { - if (actionClass.equals(a.getClass())) { - sequence.add(a); - return this; - } - } - throw new IllegalArgumentException("No action of class " + actionClass + " found."); - } - - public SequenceUI confirmYes() { - sequence.add(Boolean.TRUE); - return this; - } - - public SequenceUI confirmNo() { - sequence.add(Boolean.FALSE); - return this; - } - - public SequenceUI input(String input) { - sequence.add(input); - return this; - } - - public SequenceUI input(File... f) { - sequence.add(f); - return this; - } - - public SequenceUI output(File f) { - sequence.add(f); - return this; - } - - public SequenceUI tableRow(int i) { - sequence.add(i); - return this; - } - - private class ClientSelector { - private String pkg; - - public ClientSelector(String pkg) { - this.pkg = pkg; - } - - public Client getClient() { - for (int i = 0; i < clientListModel.getSize(); i++) { - ClientData cd = clientListModel.getElementAt(i).getClientData(); - if (cd != null) { - String s = cd.getClientDescription(); - if (pkg.equals(s)) { - return clientListModel.getElementAt(i); - } - } - } - throw new RuntimeException("Didn't find client " + pkg); - } - } - - public SequenceUI client(String pkg) { - sequence.add(new ClientSelector(pkg)); - return this; - } - - public SequenceUI choice(String pattern) { - sequence.add(pattern); - return this; - } - - @Override - public void ready() { - // Run the actions. - // No iterator or foreach loop as the sequence will be emptied while running. - try { - while (!sequence.isEmpty()) { - Object next = sequence.remove(0); - if (next instanceof Action) { - ((Action)next).actionPerformed(null); - } else { - throw new IllegalStateException("Didn't expect a non-action: " + next); - } - } - } catch (Exception e) { - e.printStackTrace(System.out); - } - - // Now shut down. - System.exit(0); - } - - @Override - public Client getSelectedClient() { - Object next = sequence.remove(0); - if (next instanceof ClientSelector) { - return ((ClientSelector)next).getClient(); - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public int getSelectedDataTableRow() { - Object next = sequence.remove(0); - if (next instanceof Integer) { - return ((Integer)next).intValue(); - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public void showWaitDialog() { - } - - @Override - public void updateWaitDialog(String s) { - System.out.println(s); - } - - @Override - public void hideWaitDialog() { - } - - @Override - public void showMessageDialog(String s) { - System.out.println(s); - } - - @Override - public boolean showConfirmDialog(String title, String message) { - Object next = sequence.remove(0); - if (next instanceof Boolean) { - return ((Boolean)next).booleanValue(); - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public String showInputDialog(String message) { - Object next = sequence.remove(0); - if (next instanceof String) { - return (String)next; - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public <T> T showChoiceDialog(String title, String message, T[] choices) { - Object next = sequence.remove(0); - if (next instanceof String) { - String s = (String)next; - for (T t : choices) { - if (t.toString().contains(s)) { - return t; - } - } - return null; - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public File showSaveDialog() { - Object next = sequence.remove(0); - if (next instanceof File) { - System.out.println(next); - return (File)next; - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public File[] showOpenDialog(boolean multi) { - Object next = sequence.remove(0); - if (next instanceof File[]) { - return (File[])next; - } - throw new IllegalStateException("Unexpected: " + next); - } - -} diff --git a/tools/preload2/src/com/android/preload/ui/SwingUI.java b/tools/preload2/src/com/android/preload/ui/SwingUI.java deleted file mode 100644 index cab3744ad74c..000000000000 --- a/tools/preload2/src/com/android/preload/ui/SwingUI.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.preload.ui; - -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.io.File; -import java.util.List; - -import javax.swing.Action; -import javax.swing.DefaultListCellRenderer; -import javax.swing.JDialog; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.JToolBar; -import javax.swing.ListModel; -import javax.swing.SwingUtilities; -import javax.swing.table.TableModel; - -public class SwingUI extends JFrame implements IUI { - - private JList<Client> clientList; - private JTable dataTable; - - // Shared file chooser, means the directory is retained. - private JFileChooser jfc; - - public SwingUI() { - super("Preloaded-classes computation"); - } - - @Override - public boolean isSingleThreaded() { - return false; - } - - @Override - public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel, - List<Action> actions) { - getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)), - BorderLayout.WEST); - clientList.setCellRenderer(new ClientListCellRenderer()); - // clientList.addListSelectionListener(listener); - - dataTable = new JTable(dataTableModel); - getContentPane().add(new JScrollPane(dataTable), BorderLayout.CENTER); - - JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL); - for (Action a : actions) { - if (a == null) { - toolbar.addSeparator(); - } else { - toolbar.add(a); - } - } - getContentPane().add(toolbar, BorderLayout.PAGE_START); - - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setBounds(100, 100, 800, 600); - - setVisible(true); - } - - @Override - public void ready() { - } - - @Override - public Client getSelectedClient() { - return clientList.getSelectedValue(); - } - - @Override - public int getSelectedDataTableRow() { - return dataTable.getSelectedRow(); - } - - private JDialog currentWaitDialog = null; - - @Override - public void showWaitDialog() { - if (currentWaitDialog == null) { - currentWaitDialog = new JDialog(this, "Please wait...", true); - currentWaitDialog.getContentPane().add(new JLabel("Please be patient."), - BorderLayout.CENTER); - JProgressBar progress = new JProgressBar(JProgressBar.HORIZONTAL); - progress.setIndeterminate(true); - currentWaitDialog.getContentPane().add(progress, BorderLayout.SOUTH); - currentWaitDialog.setSize(200, 100); - currentWaitDialog.setLocationRelativeTo(null); - showWaitDialogLater(); - } - } - - private void showWaitDialogLater() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(true); // This is blocking. - } - } - }); - } - - @Override - public void updateWaitDialog(String s) { - if (currentWaitDialog != null) { - ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s); - Dimension prefSize = currentWaitDialog.getPreferredSize(); - Dimension curSize = currentWaitDialog.getSize(); - if (prefSize.width > curSize.width || prefSize.height > curSize.height) { - currentWaitDialog.setSize(Math.max(prefSize.width, curSize.width), - Math.max(prefSize.height, curSize.height)); - currentWaitDialog.invalidate(); - } - } - } - - @Override - public void hideWaitDialog() { - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - currentWaitDialog = null; - } - } - - @Override - public void showMessageDialog(String s) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try { - JOptionPane.showMessageDialog(this, s); - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - public boolean showConfirmDialog(String title, String message) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try { - return JOptionPane.showConfirmDialog(this, title, message, JOptionPane.YES_NO_OPTION) - == JOptionPane.YES_OPTION; - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - public String showInputDialog(String message) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try { - return JOptionPane.showInputDialog(message); - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - @SuppressWarnings("unchecked") - public <T> T showChoiceDialog(String title, String message, T[] choices) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try{ - return (T)JOptionPane.showInputDialog(this, - title, - message, - JOptionPane.QUESTION_MESSAGE, - null, - choices, - choices[0]); - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - public File showSaveDialog() { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try{ - if (jfc == null) { - jfc = new JFileChooser(); - } - - int ret = jfc.showSaveDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) { - return jfc.getSelectedFile(); - } else { - return null; - } - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - public File[] showOpenDialog(boolean multi) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try{ - if (jfc == null) { - jfc = new JFileChooser(); - } - - jfc.setMultiSelectionEnabled(multi); - int ret = jfc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) { - return jfc.getSelectedFiles(); - } else { - return null; - } - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - private class ClientListCellRenderer extends DefaultListCellRenderer { - - @Override - public Component getListCellRendererComponent(JList<?> list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - ClientData cd = ((Client) value).getClientData(); - String s = cd.getClientDescription() + " (pid " + cd.getPid() + ")"; - return super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus); - } - } -} |