diff --git a/Android.mk b/Android.mk index 99e0c464bc951..03651fa7f95b6 100644 --- a/Android.mk +++ b/Android.mk @@ -72,6 +72,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IAppTask.aidl \ core/java/android/app/ITaskStackListener.aidl \ core/java/android/app/IBackupAgent.aidl \ + core/java/android/app/IBatteryService.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/IProcessObserver.aidl \ @@ -103,7 +104,6 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothA2dpSink.aidl \ core/java/android/bluetooth/IBluetoothAvrcpController.aidl \ core/java/android/bluetooth/IBluetoothCallback.aidl \ - core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl \ core/java/android/bluetooth/IBluetoothHeadset.aidl \ core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl \ core/java/android/bluetooth/IBluetoothHealth.aidl \ @@ -117,9 +117,12 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothSap.aidl \ core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \ core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \ + core/java/android/bluetooth/IBluetoothHidDevice.aidl \ + core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl \ core/java/android/bluetooth/IBluetoothGatt.aidl \ core/java/android/bluetooth/IBluetoothGattCallback.aidl \ core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \ + core/java/android/bluetooth/IBluetoothDun.aidl \ core/java/android/content/IClipboard.aidl \ core/java/android/content/IContentService.aidl \ core/java/android/content/IIntentReceiver.aidl \ @@ -247,6 +250,7 @@ LOCAL_SRC_FILES += \ core/java/android/service/voice/IVoiceInteractionService.aidl \ core/java/android/service/voice/IVoiceInteractionSession.aidl \ core/java/android/service/voice/IVoiceInteractionSessionService.aidl \ + core/java/android/service/gesture/IGestureService.aidl \ core/java/android/service/wallpaper/IWallpaperConnection.aidl \ core/java/android/service/wallpaper/IWallpaperEngine.aidl \ core/java/android/service/wallpaper/IWallpaperService.aidl \ @@ -322,6 +326,8 @@ LOCAL_SRC_FILES += \ location/java/android/location/IGeofenceProvider.aidl \ location/java/android/location/IGpsMeasurementsListener.aidl \ location/java/android/location/IGpsNavigationMessageListener.aidl \ + location/java/android/location/IGeoFencer.aidl \ + location/java/android/location/IGeoFenceListener.aidl \ location/java/android/location/IGpsStatusListener.aidl \ location/java/android/location/IGpsStatusProvider.aidl \ location/java/android/location/ILocationListener.aidl \ @@ -405,6 +411,7 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/ITelephony.aidl \ telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ + telephony/java/com/android/internal/telephony/IExtTelephony.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ @@ -413,6 +420,7 @@ LOCAL_SRC_FILES += \ packages/services/PacProcessor/com/android/net/IProxyService.aidl \ packages/services/Proxy/com/android/net/IProxyCallback.aidl \ packages/services/Proxy/com/android/net/IProxyPortListener.aidl \ + ../../vendor/cmsdk/sdk/src/java/org/cyanogenmod/internal/themes/IIconCacheManager.aidl \ # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) @@ -565,6 +573,7 @@ aidl_files := \ frameworks/base/core/java/android/app/usage/UsageEvents.aidl \ frameworks/base/core/java/android/app/Notification.aidl \ frameworks/base/core/java/android/app/NotificationManager.aidl \ + frameworks/base/core/java/android/app/NotificationGroup.aidl \ frameworks/base/core/java/android/app/WallpaperInfo.aidl \ frameworks/base/core/java/android/app/AppOpsManager.aidl \ frameworks/base/core/java/android/app/ActivityManager.aidl \ @@ -1033,7 +1042,7 @@ LOCAL_STATIC_JAVA_LIBRARIES:=$(framework_docs_LOCAL_STATIC_JAVA_LIBRARIES) LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH) -LOCAL_DROIDDOC_HTML_DIR:=docs/html-ndk +LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR) LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR) LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) # specify a second html input dir and an output path relative to OUT_DIR) diff --git a/CleanSpec.mk b/CleanSpec.mk index 48be749a5b370..fe7284e89a84c 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -239,3 +239,12 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew # ****************************************************************** # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER # ****************************************************************** + +# clean steps for aapt +$(call add-clean-step, rm -rf $(OUT_DIR)/host/$(HOST_PREBUILT_TAG)/bin/aapt) +$(call add-clean-step, rm -rf $(OUT_DIR)/host/$(HOST_PREBUILT_TAG)/obj32/EXECUTABLES/aapt_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/host/$(HOST_PREBUILT_TAG)/bin/libaapt_tests) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/aapt_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/aapt) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/bin/aapt) +$(call add-clean-step, rm -rf $(OUT_DIR)/host/$(HOST_PREBUILT_TAG)/obj32/STATIC_LIBRARIES/libaapt_intermediates) diff --git a/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java b/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java index 3ec63b429a28e..8dffe47123101 100644 --- a/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java +++ b/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java @@ -47,7 +47,7 @@ public void onShowUsage(PrintStream out) { + " appops reset [--user ] []\n" + " an Android package name.\n" + " an AppOps operation.\n" - + " one of allow, ignore, deny, or default\n" + + " one of allow, ignore, deny, default, or ask\n" + " the user id under which the package is installed. If --user is not\n" + " specified, the current user is assumed.\n"); } @@ -85,6 +85,7 @@ public void onRun() throws Exception { private static final String MODE_DENY = "deny"; private static final String MODE_IGNORE = "ignore"; private static final String MODE_DEFAULT = "default"; + private static final String MODE_ASK = "ask"; private int strOpToOp(String op) { try { @@ -154,6 +155,9 @@ private void runSet() throws Exception { case MODE_DEFAULT: modeInt = AppOpsManager.MODE_DEFAULT; break; + case MODE_ASK: + modeInt = AppOpsManager.MODE_ASK; + break; default: System.err.println("Error: Mode " + mode + " is not valid,"); return; @@ -242,6 +246,9 @@ private void runGet() throws Exception { case AppOpsManager.MODE_DEFAULT: System.out.print("default"); break; + case AppOpsManager.MODE_ASK: + System.out.print("ask"); + break; default: System.out.print("mode="); System.out.print(ent.getMode()); diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 2ee586f2c3ca7..1715728c570d9 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -23,7 +23,32 @@ LOCAL_SHARED_LIBRARIES := \ libEGL \ libGLESv1_CM \ libgui \ - libtinyalsa + libtinyalsa \ + libmedia + +ifeq ($(TARGET_CONTINUOUS_SPLASH_ENABLED),true) + LOCAL_CFLAGS += -DCONTINUOUS_SPLASH +endif + +ifeq ($(TARGET_BOOTANIMATION_PRELOAD),true) + LOCAL_CFLAGS += -DPRELOAD_BOOTANIMATION +endif + +ifeq ($(TARGET_BOOTANIMATION_TEXTURE_CACHE),true) + LOCAL_CFLAGS += -DNO_TEXTURE_CACHE=0 +endif + +ifeq ($(TARGET_BOOTANIMATION_TEXTURE_CACHE),false) + LOCAL_CFLAGS += -DNO_TEXTURE_CACHE=1 +endif + +ifeq ($(TARGET_BOOTANIMATION_USE_RGB565),true) + LOCAL_CFLAGS += -DUSE_565 +endif + +ifneq ($(TARGET_BOOTANIMATION_MULTITHREAD_DECODE),false) + LOCAL_CFLAGS += -DMULTITHREAD_DECODE +endif LOCAL_MODULE:= bootanimation diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 8f361ce1a7795..8878d058656f7 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +25,9 @@ #include #include #include +#include +#include +#include #include @@ -54,12 +58,29 @@ #include #include +#include +#include +#include + #include "BootAnimation.h" #include "AudioPlayer.h" #define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip" #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip" +#define THEME_BOOTANIMATION_FILE "/data/system/theme/bootanimation.zip" + +#define OEM_SHUTDOWN_ANIMATION_FILE "/oem/media/shutdownanimation.zip" +#define SYSTEM_SHUTDOWN_ANIMATION_FILE "/system/media/shutdownanimation.zip" +#define SYSTEM_ENCRYPTED_SHUTDOWN_ANIMATION_FILE "/system/media/shutdownanimation-encrypted.zip" +#define THEME_SHUTDOWN_ANIMATION_FILE "/data/system/theme/shutdownanimation.zip" + +#define OEM_BOOT_MUSIC_FILE "/oem/media/boot.wav" +#define SYSTEM_BOOT_MUSIC_FILE "/system/media/boot.wav" + +#define OEM_SHUTDOWN_MUSIC_FILE "/oem/media/shutdown.wav" +#define SYSTEM_SHUTDOWN_MUSIC_FILE "/system/media/shutdown.wav" + #define EXIT_PROP_NAME "service.bootanim.exit" namespace android { @@ -68,6 +89,92 @@ static const int ANIM_ENTRY_NAME_MAX = 256; // --------------------------------------------------------------------------- +static pthread_mutex_t mp_lock; +static pthread_cond_t mp_cond; +static bool isMPlayerPrepared = false; +static bool isMPlayerCompleted = false; + +#ifdef MULTITHREAD_DECODE +static const int MAX_DECODE_THREADS = 2; +static const int MAX_DECODE_CACHE = 3; +#endif + +class MPlayerListener : public MediaPlayerListener +{ + void notify(int msg, int /*ext1*/, int /*ext2*/, const Parcel * /*obj*/) + { + switch (msg) { + case MEDIA_NOP: // interface test message + break; + case MEDIA_PREPARED: + pthread_mutex_lock(&mp_lock); + isMPlayerPrepared = true; + pthread_cond_signal(&mp_cond); + pthread_mutex_unlock(&mp_lock); + break; + case MEDIA_PLAYBACK_COMPLETE: + pthread_mutex_lock(&mp_lock); + isMPlayerCompleted = true; + pthread_cond_signal(&mp_cond); + pthread_mutex_unlock(&mp_lock); + break; + default: + break; + } + } +}; + +static unsigned long getFreeMemory(void) +{ + int fd = open("/proc/meminfo", O_RDONLY); + const char* const sums[] = { "MemFree:", "Cached:", NULL }; + const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 }; + unsigned int num = 2; + + if (fd < 0) { + ALOGW("Unable to open /proc/meminfo"); + return -1; + } + + char buffer[256]; + const int len = read(fd, buffer, sizeof(buffer)-1); + close(fd); + + if (len < 0) { + ALOGW("Unable to read /proc/meminfo"); + return -1; + } + buffer[len] = 0; + + size_t numFound = 0; + unsigned long mem = 0; + + char* p = buffer; + while (*p && numFound < num) { + int i = 0; + while (sums[i]) { + if (strncmp(p, sums[i], sumsLen[i]) == 0) { + p += sumsLen[i]; + while (*p == ' ') p++; + char* num = p; + while (*p >= '0' && *p <= '9') p++; + if (*p != 0) { + *p = 0; + p++; + if (*p == 0) p--; + } + mem += atoll(num); + numFound++; + break; + } + i++; + } + p++; + } + + return numFound > 0 ? mem : -1; +} + BootAnimation::BootAnimation() : Thread(false), mZip(NULL) { mSession = new SurfaceComposerClient(); @@ -161,33 +268,42 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, return NO_ERROR; } -status_t BootAnimation::initTexture(const Animation::Frame& frame) +SkBitmap* BootAnimation::decode(const Animation::Frame& frame) { - //StopWatch watch("blah"); - SkBitmap bitmap; + SkBitmap *bitmap = NULL; SkMemoryStream stream(frame.map->getDataPtr(), frame.map->getDataLength()); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (codec != NULL) { + bitmap = new SkBitmap(); codec->setDitherImage(false); - codec->decode(&stream, &bitmap, + codec->decode(&stream, bitmap, + #ifdef USE_565 + kRGB_565_SkColorType, + #else kN32_SkColorType, + #endif SkImageDecoder::kDecodePixels_Mode); delete codec; } - // FileMap memory is never released until application exit. - // Release it now as the texture is already loaded and the memory used for - // the packed resource can be released. - delete frame.map; + return bitmap; +} - // ensure we can call getPixels(). No need to call unlock, since the - // bitmap will go out of scope when we return from this method. - bitmap.lockPixels(); +status_t BootAnimation::initTexture(const Animation::Frame& frame) +{ + //StopWatch watch("blah"); + return initTexture(decode(frame)); +} - const int w = bitmap.width(); - const int h = bitmap.height(); - const void* p = bitmap.getPixels(); +status_t BootAnimation::initTexture(SkBitmap *bitmap) +{ + // ensure we can call getPixels(). + bitmap->lockPixels(); + + const int w = bitmap->width(); + const int h = bitmap->height(); + const void* p = bitmap->getPixels(); GLint crop[4] = { 0, h, w, -h }; int tw = 1 << (31 - __builtin_clz(w)); @@ -195,7 +311,7 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame) if (tw < w) tw <<= 1; if (th < h) th <<= 1; - switch (bitmap.colorType()) { + switch (bitmap->colorType()) { case kN32_SkColorType: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, @@ -225,6 +341,8 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame) glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + bitmap->unlockPixels(); + delete bitmap; return NO_ERROR; } @@ -237,6 +355,18 @@ status_t BootAnimation::readyToRun() { status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); if (status) return -1; + char value[PROPERTY_VALUE_MAX]; + property_get("persist.panel.orientation", value, "0"); + int orient = atoi(value) / 90; + + if(orient == eOrientation90 || orient == eOrientation270) { + int temp = dinfo.h; + dinfo.h = dinfo.w; + dinfo.w = temp; + } + + Rect destRect(dinfo.w, dinfo.h); + mSession->setDisplayProjection(dtoken, orient, destRect, destRect); // create the native surface sp control = session()->createSurface(String8("BootAnimation"), @@ -287,21 +417,54 @@ status_t BootAnimation::readyToRun() { char decrypt[PROPERTY_VALUE_MAX]; property_get("vold.decrypt", decrypt, ""); + // Use customized resources for boot and showdown animation + // instead of system predefined boot animation files. bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); ZipFileRO* zipFile = NULL; if ((encryptedAnimation && - (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && - ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) || + (access(getAnimationFileName(IMG_ENC), R_OK) == 0) && + ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_ENC))) != NULL)) || + + ((access(getAnimationFileName(IMG_THM), R_OK) == 0) && + ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_THM))) != NULL)) || - ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) && - ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) || + ((access(getAnimationFileName(IMG_DATA), R_OK) == 0) && + ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_DATA))) != NULL)) || - ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && - ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) { + ((access(getAnimationFileName(IMG_SYS), R_OK) == 0) && + ((zipFile = ZipFileRO::open(getAnimationFileName(IMG_SYS))) != NULL))) { mZip = zipFile; } +#ifdef PRELOAD_BOOTANIMATION + // Preload the bootanimation zip on memory, so we don't stutter + // when showing the animation + FILE* fd; + if (encryptedAnimation && access(getAnimationFileName(IMG_ENC), R_OK) == 0) + fd = fopen(getAnimationFileName(IMG_ENC), "r"); + else if (access(getAnimationFileName(IMG_THM), R_OK) == 0) + fd = fopen(getAnimationFileName(IMG_THM), "r"); + else if (access(getAnimationFileName(IMG_DATA), R_OK) == 0) + fd = fopen(getAnimationFileName(IMG_DATA), "r"); + else if (access(getAnimationFileName(IMG_SYS), R_OK) == 0) + fd = fopen(getAnimationFileName(IMG_SYS), "r"); + else + return NO_ERROR; + + if (fd != NULL) { + // Since including fcntl.h doesn't give us the wrapper, use the syscall. + // 32 bits takes LO/HI offset (we don't care about endianness of 0). +#if defined(__aarch64__) || defined(__x86_64__) + if (syscall(__NR_readahead, fd, 0, INT_MAX)) +#else + if (syscall(__NR_readahead, fd, 0, 0, INT_MAX)) +#endif + ALOGW("Unable to cache the animation"); + fclose(fd); + } +#endif + return NO_ERROR; } @@ -452,6 +615,7 @@ bool BootAnimation::readFile(const char* name, String8& outString) bool BootAnimation::movie() { + char value[PROPERTY_VALUE_MAX]; String8 desString; if (!readFile("desc.txt", desString)) { @@ -481,11 +645,16 @@ bool BootAnimation::movie() char path[ANIM_ENTRY_NAME_MAX]; char color[7] = "000000"; // default to black if unspecified + char value[PROPERTY_VALUE_MAX]; char pathType; + + property_get("persist.bootanimation.scale", value, "1"); + double bas = atof(value); + if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps); - animation.width = width; - animation.height = height; + animation.width = width * bas; + animation.height = height * bas; animation.fps = fps; } else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) { @@ -556,11 +725,14 @@ bool BootAnimation::movie() mZip->endIteration(cookie); +#ifndef CONTINUOUS_SPLASH glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); +#endif + glBindTexture(GL_TEXTURE_2D, 0); glEnable(GL_TEXTURE_2D); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); @@ -576,11 +748,45 @@ bool BootAnimation::movie() Region clearReg(Rect(mWidth, mHeight)); clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); + pthread_mutex_init(&mp_lock, NULL); + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + pthread_cond_init(&mp_cond, &attr); + for (size_t i=0 ; i 48 * 1024 * 1024) ? 1 : 0; + #endif + glBindTexture(GL_TEXTURE_2D, 0); + /*calculate if we need to runtime save memory + * condition: runtime free memory is less than the textures that will used. + * needSaveMem default to be false + */ + GLint mMaxTextureSize; + bool needSaveMem = false; + GLuint mTextureid; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + //ALOGD("freemem:%ld, %d", getFreeMemory(), mMaxTextureSize); + if(getFreeMemory() < mMaxTextureSize * mMaxTextureSize * fcount / 1024 || noTextureCache) { + ALOGD("Use save memory method, maybe small fps in actual."); + needSaveMem = true; + glGenTextures(1, &mTextureid); + glBindTexture(GL_TEXTURE_2D, mTextureid); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + } + for (int r=0 ; !part.count || r 0) { + if (r > 0 && !needSaveMem) { glBindTexture(GL_TEXTURE_2D, frame.tid); } else { - if (part.count != 1) { + if (!needSaveMem && part.count != 1) { glGenTextures(1, &frame.tid); glBindTexture(GL_TEXTURE_2D, frame.tid); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } +#ifdef MULTITHREAD_DECODE + initTexture(frameManager->next()); +#else initTexture(frame); +#endif } if (!clearReg.isEmpty()) { @@ -651,24 +869,302 @@ bool BootAnimation::movie() usleep(part.pause * ns2us(frameDuration)); +#ifdef MULTITHREAD_DECODE + if (frameManager) { + delete frameManager; + } +#endif + // For infinite parts, we've now played them at least once, so perhaps exit if(exitPending() && !part.count) break; } // free the textures for this part - if (part.count != 1) { + if (!needSaveMem && part.count != 1) { for (size_t j=0 ; j mp = new MediaPlayer(); + sp mListener = new MPlayerListener(); + if (mp != NULL) { + ALOGD("starting to play %s", fileName); + mp->setListener(mListener); + + if (mp->setDataSource(NULL, fileName, NULL) == NO_ERROR) { + mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE); + mp->prepare(); + } else { + ALOGE("failed to setDataSource for %s", fileName); + return NULL; + } + + //waiting for media player is prepared. + pthread_mutex_lock(&mp_lock); + while (!isMPlayerPrepared) { + pthread_cond_wait(&mp_cond, &mp_lock); + } + pthread_mutex_unlock(&mp_lock); + + audio_devices_t device = AudioSystem::getDevicesForStream(AUDIO_STREAM_ENFORCED_AUDIBLE); + AudioSystem::initStreamVolume(AUDIO_STREAM_ENFORCED_AUDIBLE,0,7); + AudioSystem::setStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, 7, device); + + AudioSystem::getStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, &index, device); + if (index != 0) { + ALOGD("playing %s", fileName); + mp->seekTo(0); + mp->start(); + } else { + ALOGW("current volume is zero."); + } + } + return NULL; +} + +void BootAnimation::playBackgroundMusic(void) +{ + //Shutdown music is playing in ShutdownThread.java + if (!checkBootState()) { + return; + } + + /* Make sure sound cards are populated */ + FILE* fp = NULL; + if ((fp = fopen("/proc/asound/cards", "r")) == NULL) { + ALOGW("Cannot open /proc/asound/cards file to get sound card info."); + } + + char value[PROPERTY_VALUE_MAX]; + property_get("qcom.audio.init", value, "null"); + if (strncmp(value, "complete", 8) != 0) { + ALOGW("Audio service is not initiated."); + } + + fclose(fp); + + const char *fileName; + if (((fileName = getBootRingtoneFileName(IMG_DATA)) != NULL && access(fileName, R_OK) == 0) || + ((fileName = getBootRingtoneFileName(IMG_SYS)) != NULL + && access(fileName, R_OK) == 0)) { + pthread_t tid; + pthread_create(&tid, NULL, playMusic, (void *)fileName); + pthread_join(tid, NULL); + } +} +bool BootAnimation::checkBootState(void) +{ + char value[PROPERTY_VALUE_MAX]; + bool ret = true; + + property_get("sys.shutdown.requested", value, "null"); + if (strncmp(value, "null", 4) != 0) { + ret = false; + } + + return ret; +} + +#ifdef MULTITHREAD_DECODE + +FrameManager::FrameManager(int numThreads, size_t maxSize, const SortedVector& frames) : + mMaxSize(maxSize), + mFrameCounter(0), + mNextIdx(0), + mFrames(frames), + mExit(false) +{ + pthread_mutex_init(&mBitmapsMutex, NULL); + pthread_cond_init(&mSpaceAvailableCondition, NULL); + pthread_cond_init(&mBitmapReadyCondition, NULL); + for (int i = 0; i < numThreads; i++) { + DecodeThread *thread = new DecodeThread(this); + thread->run("bootanimation", PRIORITY_URGENT_DISPLAY); + mThreads.add(thread); + } +} + +FrameManager::~FrameManager() +{ + mExit = true; + pthread_cond_broadcast(&mSpaceAvailableCondition); + pthread_cond_broadcast(&mBitmapReadyCondition); + for (size_t i = 0; i < mThreads.size(); i++) { + mThreads.itemAt(i)->requestExitAndWait(); + } + + // Any bitmap left in the queue won't get cleaned up by + // the consumer. Clean up now. + for(size_t i = 0; i < mDecodedFrames.size(); i++) { + delete mDecodedFrames[i].bitmap; + } +} + +SkBitmap* FrameManager::next() +{ + pthread_mutex_lock(&mBitmapsMutex); + + while(mDecodedFrames.size() == 0 || + mDecodedFrames.itemAt(0).idx != mNextIdx) { + pthread_cond_wait(&mBitmapReadyCondition, &mBitmapsMutex); + } + DecodeWork work = mDecodedFrames.itemAt(0); + mDecodedFrames.removeAt(0); + mNextIdx++; + pthread_cond_signal(&mSpaceAvailableCondition); + pthread_mutex_unlock(&mBitmapsMutex); + // The caller now owns the bitmap + return work.bitmap; +} + +FrameManager::DecodeWork FrameManager::getWork() +{ + DecodeWork work = { + .frame = NULL, + .bitmap = NULL, + .idx = 0 + }; + + pthread_mutex_lock(&mBitmapsMutex); + + while(mDecodedFrames.size() >= mMaxSize && !mExit) { + pthread_cond_wait(&mSpaceAvailableCondition, &mBitmapsMutex); + } + + if (!mExit) { + work.frame = &mFrames.itemAt(mFrameCounter % mFrames.size()); + work.idx = mFrameCounter; + mFrameCounter++; + } + + pthread_mutex_unlock(&mBitmapsMutex); + return work; +} + +void FrameManager::completeWork(DecodeWork work) { + size_t insertIdx; + pthread_mutex_lock(&mBitmapsMutex); + + for(insertIdx = 0; insertIdx < mDecodedFrames.size(); insertIdx++) { + if (work.idx < mDecodedFrames.itemAt(insertIdx).idx) { + break; + } + } + + mDecodedFrames.insertAt(work, insertIdx); + pthread_cond_signal(&mBitmapReadyCondition); + + pthread_mutex_unlock(&mBitmapsMutex); +} + +FrameManager::DecodeThread::DecodeThread(FrameManager* manager) : + Thread(false), + mManager(manager) +{ + +} + +bool FrameManager::DecodeThread::threadLoop() +{ + DecodeWork work = mManager->getWork(); + if (work.frame != NULL) { + work.bitmap = BootAnimation::decode(*work.frame); + mManager->completeWork(work); + return true; } return false; } +#endif + // --------------------------------------------------------------------------- -} -; // namespace android +}; // namespace android diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index f968b255d37aa..090894f7addd5 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -26,6 +26,8 @@ #include #include +#include + class SkBitmap; namespace android { @@ -34,12 +36,24 @@ class AudioPlayer; class Surface; class SurfaceComposerClient; class SurfaceControl; +#ifdef MULTITHREAD_DECODE +class FrameManager; +#endif // --------------------------------------------------------------------------- class BootAnimation : public Thread, public IBinder::DeathRecipient { +#ifdef MULTITHREAD_DECODE + friend class FrameManager; +#endif public: + enum { + eOrientationDefault = 0, + eOrientation90 = 1, + eOrientation180 = 2, + eOrientation270 = 3, + }; BootAnimation(); virtual ~BootAnimation(); @@ -83,16 +97,25 @@ class BootAnimation : public Thread, public IBinder::DeathRecipient status_t initTexture(Texture* texture, AssetManager& asset, const char* name); status_t initTexture(const Animation::Frame& frame); + status_t initTexture(SkBitmap *bitmap); bool android(); bool readFile(const char* name, String8& outString); bool movie(); + enum ImageID { IMG_DATA = 0, IMG_SYS = 1, IMG_ENC = 2, IMG_THM = 3 }; + const char *getAnimationFileName(ImageID image); + const char *getBootRingtoneFileName(ImageID image); + void playBackgroundMusic(); + bool checkBootState(); void checkExit(); + void checkShowAndroid(); + + static SkBitmap *decode(const Animation::Frame& frame); sp mSession; sp mAudioPlayer; AssetManager mAssets; - Texture mAndroid[2]; + Texture mAndroid[3]; int mWidth; int mHeight; EGLDisplay mDisplay; @@ -103,6 +126,50 @@ class BootAnimation : public Thread, public IBinder::DeathRecipient ZipFileRO *mZip; }; +#ifdef MULTITHREAD_DECODE + +class FrameManager { +public: + struct DecodeWork { + const BootAnimation::Animation::Frame *frame; + SkBitmap *bitmap; + size_t idx; + }; + + FrameManager(int numThreads, size_t maxSize, const SortedVector& frames); + virtual ~FrameManager(); + + SkBitmap* next(); + +protected: + DecodeWork getWork(); + void completeWork(DecodeWork work); + +private: + + class DecodeThread : public Thread { + public: + DecodeThread(FrameManager* manager); + virtual ~DecodeThread() {} + private: + virtual bool threadLoop(); + FrameManager *mManager; + }; + + size_t mMaxSize; + size_t mFrameCounter; + size_t mNextIdx; + const SortedVector& mFrames; + Vector mDecodedFrames; + pthread_mutex_t mBitmapsMutex; + pthread_cond_t mSpaceAvailableCondition; + pthread_cond_t mBitmapReadyCondition; + bool mExit; + Vector > mThreads; +}; + +#endif + // --------------------------------------------------------------------------- }; // namespace android diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp index 48a34e7dbf627..50e4b1f561a79 100644 --- a/cmds/bootanimation/bootanimation_main.cpp +++ b/cmds/bootanimation/bootanimation_main.cpp @@ -38,8 +38,9 @@ int main() property_get("debug.sf.nobootanimation", value, "0"); int noBootAnimation = atoi(value); ALOGI_IF(noBootAnimation, "boot animation disabled"); - if (!noBootAnimation) { + property_get("ro.alarm_boot", value, "false"); + if (!noBootAnimation && strcmp(value, "true")) { sp proc(ProcessState::self()); ProcessState::self()->startThreadPool(); diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java index c0ed8935dc2c7..1d8b8b1b5bcca 100644 --- a/cmds/content/src/com/android/commands/content/Content.java +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -103,10 +103,12 @@ public class Content { + "--where \"name=\'new_setting\'\"\n" + "\n" + "usage: adb shell content query --uri [--user ]" - + " [--projection ] [--where ] [--sort ]\n" + + " [--projection ] [--where ] [--sort ] " + + " [--show-type ] \n" + " is a list of colon separated column names and is formatted:\n" + " [:...]\n" + " is the order in which rows in the result should be sorted.\n" + + " if true shows the type of value of each projection column" + " Example:\n" + " # Select \"name\" and \"value\" columns from secure settings where \"name\" is " + "equal to \"new_setting\" and sort the result by name in ascending order.\n" @@ -142,6 +144,7 @@ private static class Parser { private static final String ARGUMENT_METHOD = "--method"; private static final String ARGUMENT_ARG = "--arg"; private static final String ARGUMENT_EXTRA = "--extra"; + private static final String ARGUMENT_SHOW_TYPE = "--show-type"; private static final String TYPE_BOOLEAN = "b"; private static final String TYPE_STRING = "s"; private static final String TYPE_INTEGER = "i"; @@ -316,6 +319,7 @@ public QueryCommand parseQueryCommand() { String[] projection = null; String sort = null; String where = null; + boolean showType = false; for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); @@ -327,6 +331,8 @@ public QueryCommand parseQueryCommand() { sort = argumentValueRequired(argument); } else if (ARGUMENT_PROJECTION.equals(argument)) { projection = argumentValueRequired(argument).split("[\\s]*:[\\s]*"); + } else if (ARGUMENT_SHOW_TYPE.equals(argument)) { + showType = argumentValueRequiredForBoolean(argument); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -335,7 +341,7 @@ public QueryCommand parseQueryCommand() { throw new IllegalArgumentException("Content provider URI not specified." + " Did you specify --uri argument?"); } - return new QueryCommand(uri, userId, projection, where, sort); + return new QueryCommand(uri, userId, projection, where, sort, showType); } private void parseBindValue(ContentValues values) { @@ -367,6 +373,14 @@ private void parseBindValue(ContentValues values) { } } + private boolean argumentValueRequiredForBoolean(String argument) { + String value = mTokenizer.nextArg(); + if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) { + throw new IllegalArgumentException("No value for argument: " + argument); + } + return value.equals("true"); + } + private String argumentValueRequired(String argument) { String value = mTokenizer.nextArg(); if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) { @@ -539,12 +553,15 @@ private static void copy(InputStream is, OutputStream os) throws IOException { private static class QueryCommand extends DeleteCommand { final String[] mProjection; final String mSortOrder; + final boolean mShowType; public QueryCommand( - Uri uri, int userId, String[] projection, String where, String sortOrder) { + Uri uri, int userId, String[] projection, String where, String sortOrder, + boolean showType) { super(uri, userId, where); mProjection = projection; mSortOrder = sortOrder; + mShowType = showType; } @Override @@ -590,6 +607,7 @@ public void onExecute(IContentProvider provider) throws Exception { break; } builder.append(columnName).append("=").append(columnValue); + if (mShowType) builder.append(", type=").append(type); } System.out.println(builder); } while (cursor.moveToNext()); diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp index 41395f13ce5f4..dd0554e8033b1 100644 --- a/cmds/idmap/create.cpp +++ b/cmds/idmap/create.cpp @@ -33,6 +33,7 @@ namespace { int open_idmap(const char *path) { int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)); + bool needUnlink = true; if (fd == -1) { ALOGD("error: open %s: %s\n", path, strerror(errno)); goto fail; @@ -41,8 +42,10 @@ namespace { ALOGD("error: fchmod %s: %s\n", path, strerror(errno)); goto fail; } - if (TEMP_FAILURE_RETRY(flock(fd, LOCK_EX | LOCK_NB)) != 0) { + if (TEMP_FAILURE_RETRY(flock(fd, LOCK_EX)) != 0) { ALOGD("error: flock %s: %s\n", path, strerror(errno)); + // If the file is locked by another process, then we needn't unlink the file. + needUnlink = false; goto fail; } @@ -50,7 +53,7 @@ namespace { fail: if (fd != -1) { close(fd); - unlink(path); + if (needUnlink) unlink(path); } return -1; } @@ -150,26 +153,26 @@ namespace { } int create_idmap(const char *target_apk_path, const char *overlay_apk_path, - uint32_t **data, size_t *size) + const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, uint32_t **data, + size_t *size) { uint32_t target_crc, overlay_crc; - if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME, - &target_crc) == -1) { - return -1; - } - if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME, - &overlay_crc) == -1) { - return -1; - } + + // In the original implementation, crc of the res tables are generated + // theme apks however do not need a restable, everything is in assets/ + // instead timestamps are used + target_crc = 0; + overlay_crc = 0; AssetManager am; - bool b = am.createIdmap(target_apk_path, overlay_apk_path, target_crc, overlay_crc, - data, size); + bool b = am.createIdmap(target_apk_path, overlay_apk_path, cache_path, target_crc, + overlay_crc, target_hash, overlay_hash, data, size); return b ? 0 : -1; } int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path, - int fd, bool check_if_stale) + const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, int fd, + bool check_if_stale) { if (check_if_stale) { if (!is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd)) { @@ -181,7 +184,8 @@ namespace { uint32_t *data = NULL; size_t size; - if (create_idmap(target_apk_path, overlay_apk_path, &data, &size) == -1) { + if (create_idmap(target_apk_path, overlay_apk_path, cache_path, target_hash, overlay_hash, + &data, &size) == -1) { return -1; } @@ -196,6 +200,7 @@ namespace { } int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path, + const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, const char *idmap_path) { if (!is_idmap_stale_path(target_apk_path, overlay_apk_path, idmap_path)) { @@ -208,7 +213,8 @@ int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path, return EXIT_FAILURE; } - int r = create_and_write_idmap(target_apk_path, overlay_apk_path, fd, false); + int r = create_and_write_idmap(target_apk_path, overlay_apk_path, cache_path, + target_hash, overlay_hash, fd, false); close(fd); if (r != 0) { unlink(idmap_path); @@ -216,8 +222,10 @@ int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path, return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd) +int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, + const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, int fd) { - return create_and_write_idmap(target_apk_path, overlay_apk_path, fd, true) == 0 ? + return create_and_write_idmap(target_apk_path, overlay_apk_path, cache_path, target_hash, + overlay_hash, fd, true) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp index 90cfa2c610f00..cc3f231824a97 100644 --- a/cmds/idmap/idmap.cpp +++ b/cmds/idmap/idmap.cpp @@ -66,29 +66,31 @@ EXAMPLES \n\ Display an idmap file: \n\ \n\ $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\ - SECTION ENTRY VALUE COMMENT \n\ - IDMAP HEADER magic 0x706d6469 \n\ - base crc 0xb65a383f \n\ - overlay crc 0x7b9675e8 \n\ - base path .......... /path/to/target.apk \n\ - overlay path .......... /path/to/overlay.apk \n\ - DATA HEADER target pkg 0x0000007f \n\ - types count 0x00000003 \n\ - DATA BLOCK target type 0x00000002 \n\ - overlay type 0x00000002 \n\ - entry count 0x00000001 \n\ - entry offset 0x00000000 \n\ - entry 0x00000000 drawable/drawable \n\ - DATA BLOCK target type 0x00000003 \n\ - overlay type 0x00000003 \n\ - entry count 0x00000001 \n\ - entry offset 0x00000000 \n\ - entry 0x00000000 xml/integer \n\ - DATA BLOCK target type 0x00000004 \n\ - overlay type 0x00000004 \n\ - entry count 0x00000001 \n\ - entry offset 0x00000000 \n\ - entry 0x00000000 raw/lorem_ipsum \n\ + SECTION ENTRY VALUE COMMENT \n\ + IDMAP HEADER magic 0x706d6469 \n\ + base crc 0xb65a383f \n\ + overlay crc 0x7b9675e8 \n\ + base mtime 0x1eb47d51 \n\ + overlay mtime 0x185f87a2 \n\ + base path .......... /path/to/target.apk \n\ + overlay path .......... /path/to/overlay.apk \n\ + DATA HEADER target pkg 0x0000007f \n\ + types count 0x00000003 \n\ + DATA BLOCK target type 0x00000002 \n\ + overlay type 0x00000002 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 drawable/drawable \n\ + DATA BLOCK target type 0x00000003 \n\ + overlay type 0x00000003 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 xml/integer \n\ + DATA BLOCK target type 0x00000004 \n\ + overlay type 0x00000004 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 raw/lorem_ipsum \n\ \n\ In this example, the overlay package provides three alternative resource values:\n\ drawable/drawable, xml/integer, and raw/lorem_ipsum \n\ @@ -120,7 +122,8 @@ NOTES \n\ } int maybe_create_fd(const char *target_apk_path, const char *overlay_apk_path, - const char *idmap_str) + const char *cache_path, const char *idmap_str, const char *target_hash_str, + const char *overlay_hash_str) { // anyone (not just root or system) may do --fd -- the file has // already been opened by someone else on our behalf @@ -141,12 +144,16 @@ NOTES \n\ ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno)); return -1; } + int target_hash = strtol(target_hash_str, 0, 10); + int overlay_hash = strtol(overlay_hash_str, 0, 10); - return idmap_create_fd(target_apk_path, overlay_apk_path, idmap_fd); + return idmap_create_fd(target_apk_path, overlay_apk_path, cache_path, target_hash, + overlay_hash, idmap_fd); } int maybe_create_path(const char *target_apk_path, const char *overlay_apk_path, - const char *idmap_path) + const char *cache_path, const char *idmap_path, const char *target_hash_str, + const char *overlay_hash_str) { if (!verify_root_or_system()) { fprintf(stderr, "error: permission denied: not user root or user system\n"); @@ -163,7 +170,10 @@ NOTES \n\ return -1; } - return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path); + int target_hash = strtol(target_hash_str, 0, 10); + int overlay_hash = strtol(overlay_hash_str, 0, 10); + return idmap_create_path(target_apk_path, overlay_apk_path, cache_path, target_hash, + overlay_hash, idmap_path); } int maybe_scan(const char *overlay_dir, const char *target_package_name, @@ -222,12 +232,12 @@ int main(int argc, char **argv) return 0; } - if (argc == 5 && !strcmp(argv[1], "--fd")) { - return maybe_create_fd(argv[2], argv[3], argv[4]); + if (argc == 8 && !strcmp(argv[1], "--fd")) { + return maybe_create_fd(argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); } - if (argc == 5 && !strcmp(argv[1], "--path")) { - return maybe_create_path(argv[2], argv[3], argv[4]); + if (argc == 8 && !strcmp(argv[1], "--path")) { + return maybe_create_path(argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); } if (argc == 6 && !strcmp(argv[1], "--scan")) { diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h index f507dd8530ac3..6a9c5ef788130 100644 --- a/cmds/idmap/idmap.h +++ b/cmds/idmap/idmap.h @@ -19,9 +19,12 @@ #endif int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path, + const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, const char *idmap_path); -int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd); +int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, + const char *cache_path, uint32_t target_hash, uint32_t overlay_hash, + int fd); // Regarding target_package_name: the idmap_scan implementation should // be able to extract this from the manifest in target_apk_path, diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp index f6afc8594309a..3087e6e2035fc 100644 --- a/cmds/idmap/inspect.cpp +++ b/cmds/idmap/inspect.cpp @@ -200,6 +200,18 @@ namespace { } print("", "overlay crc", i, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "base mtime", i, ""); + + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "overlay mtime", i, ""); + err = buf.nextPath(path); if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath @@ -223,7 +235,8 @@ namespace { } status_t parse_data(IdmapBuffer& buf, const AssetManager& am) { - const uint32_t packageId = am.getResources().getBasePackageId(0); + const ResTable& rt = am.getResources(); + const uint32_t packageId = rt.getBasePackageId(rt.getBasePackageCount() - 1); uint16_t data16; status_t err = buf.nextUint16(&data16); diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp index 612a7ebb5489f..b319e68e9b6e4 100644 --- a/cmds/idmap/scan.cpp +++ b/cmds/idmap/scan.cpp @@ -25,8 +25,7 @@ namespace { bool operator<(Overlay const& rhs) const { - // Note: order is reversed by design - return rhs.priority < priority; + return rhs.priority > priority; } String8 apk_path; @@ -165,6 +164,62 @@ namespace { delete dataMap; return priority; } + + int idmap_scan(const char *overlay_dir, const char *target_package_name, + const char *target_apk_path, const char *idmap_dir, + SortedVector& overlayVector) + { + DIR *dir = opendir(overlay_dir); + if (dir == NULL) { + return EXIT_FAILURE; + } + + struct dirent *dirent; + while ((dirent = readdir(dir)) != NULL) { + struct stat st; + char overlay_apk_path[PATH_MAX + 1]; + snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name); + if (stat(overlay_apk_path, &st) < 0) { + continue; + } + if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) { + continue; + } + + if (S_ISDIR(st.st_mode)) { + String8 dir_name = String8(overlay_apk_path).getPathLeaf(); + if (dir_name == "." || dir_name == "..") { + // Skip the "." and ".." dir. + continue; + } + idmap_scan(overlay_apk_path, target_package_name, target_apk_path, idmap_dir, + overlayVector); + } else { + int priority = parse_apk(overlay_apk_path, target_package_name); + if (priority < 0) { + continue; + } + + String8 idmap_path(idmap_dir); + idmap_path.appendPath(flatten_path(overlay_apk_path + 1)); + idmap_path.append("@idmap"); + + if (idmap_create_path(target_apk_path, overlay_apk_path, NULL, 0, 0, + idmap_path.string()) != 0) { + ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n", + target_apk_path, overlay_apk_path, idmap_path.string()); + continue; + } + + Overlay overlay(String8(overlay_apk_path), idmap_path, priority); + overlayVector.add(overlay); + } + } + + closedir(dir); + + return EXIT_SUCCESS; + } } int idmap_scan(const char *overlay_dir, const char *target_package_name, @@ -176,48 +231,13 @@ int idmap_scan(const char *overlay_dir, const char *target_package_name, return EXIT_FAILURE; } - DIR *dir = opendir(overlay_dir); - if (dir == NULL) { - return EXIT_FAILURE; - } - SortedVector overlayVector; - struct dirent *dirent; - while ((dirent = readdir(dir)) != NULL) { - struct stat st; - char overlay_apk_path[PATH_MAX + 1]; - snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name); - if (stat(overlay_apk_path, &st) < 0) { - continue; - } - if (!S_ISREG(st.st_mode)) { - continue; - } - - int priority = parse_apk(overlay_apk_path, target_package_name); - if (priority < 0) { - continue; - } - - String8 idmap_path(idmap_dir); - idmap_path.appendPath(flatten_path(overlay_apk_path + 1)); - idmap_path.append("@idmap"); - - if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) { - ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n", - target_apk_path, overlay_apk_path, idmap_path.string()); - continue; - } - - Overlay overlay(String8(overlay_apk_path), idmap_path, priority); - overlayVector.add(overlay); - } - - closedir(dir); + int res = idmap_scan(overlay_dir, target_package_name, target_apk_path, idmap_dir, + overlayVector); - if (!writePackagesList(filename.string(), overlayVector)) { + if (res == EXIT_FAILURE || !writePackagesList(filename.string(), overlayVector)) { return EXIT_FAILURE; } - return EXIT_SUCCESS; + return res; } diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index 2a7c79bdfb5b5..40148c61d44c7 100644 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -47,6 +47,7 @@ public class Input { put("touchpad", InputDevice.SOURCE_TOUCHPAD); put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION); put("joystick", InputDevice.SOURCE_JOYSTICK); + put("gesture", InputDevice.SOURCE_GESTURE_SENSOR); }}; diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java index d7f23cb440989..d185b5625d4e4 100644 --- a/cmds/media/src/com/android/commands/media/Media.java +++ b/cmds/media/src/com/android/commands/media/Media.java @@ -222,6 +222,26 @@ public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteExceptio System.out.println("onVolumeInfoChanged " + info); } + @Override + public void onPlayItemResponse(boolean success) throws RemoteException { + System.out.println("onPlayItemResponse "); + } + + @Override + public void onUpdateNowPlayingEntries(long[] playList) throws RemoteException { + System.out.println("onUpdateNowPlayingEntries "); + } + + @Override + public void onUpdateFolderInfoBrowsedPlayer(String stringUri) throws RemoteException { + System.out.println("onUpdateFolderInfoBrowsedPlayer "); + } + + @Override + public void onUpdateNowPlayingContentChange() throws RemoteException { + System.out.println("onUpdateNowPlayingContentChange "); + } + void printUsageMessage() { try { System.out.println("V2Monitoring session " + mController.getTag() diff --git a/cmds/pm/pm b/cmds/pm/pm index 81838386c055e..53f85b2f9c516 100755 --- a/cmds/pm/pm +++ b/cmds/pm/pm @@ -1,3 +1,4 @@ +#!/system/bin/sh # Script to start "pm" on the device, which has a very rudimentary # shell. # diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 58c3a9ccc89e4..4869adff8f017 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -262,6 +262,10 @@ public int run(String[] args) throws IOException, RemoteException { return runMovePrimaryStorage(); } + if ("set-user-restriction".equals(op)) { + return runSetUserRestriction(); + } + try { if (args.length == 1) { if (args[0].equalsIgnoreCase("-l")) { @@ -1518,6 +1522,38 @@ public int runMovePrimaryStorage() { } } + public int runSetUserRestriction() { + int userId = UserHandle.USER_OWNER; + String opt = nextOption(); + if (opt != null && "--user".equals(opt)) { + String arg = nextArg(); + if (arg == null || !isNumber(arg)) { + System.err.println("Error: valid userId not specified"); + return 1; + } + userId = Integer.parseInt(arg); + } + + String restriction = nextArg(); + String arg = nextArg(); + boolean value; + if ("1".equals(arg)) { + value = true; + } else if ("0".equals(arg)) { + value = false; + } else { + System.err.println("Error: valid value not specified"); + return 1; + } + try { + mUm.setUserRestriction(restriction, value, userId); + return 0; + } catch (RemoteException e) { + System.err.println(e.toString()); + return 1; + } + } + private int runUninstall() throws RemoteException { int flags = 0; int userId = UserHandle.USER_ALL; diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index c469ae4a0961d..865a216c24afe 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -50,6 +50,7 @@ static void usage(const char* pname) "usage: %s [-hp] [-d display-id] [FILENAME]\n" " -h: this message\n" " -p: save the file as a png.\n" + " -j: save the file as a jpeg.\n" " -d: specify the display id to capture, default %d.\n" "If FILENAME ends with .png it will be saved as a png.\n" "If FILENAME is not given, the results will be printed to stdout.\n", @@ -112,13 +113,17 @@ int main(int argc, char** argv) const char* pname = argv[0]; bool png = false; + bool jpeg = false; int32_t displayId = DEFAULT_DISPLAY_ID; int c; - while ((c = getopt(argc, argv, "phd:")) != -1) { + while ((c = getopt(argc, argv, "pjhd:")) != -1) { switch (c) { case 'p': png = true; break; + case 'j': + jpeg = true; + break; case 'd': displayId = atoi(optarg); break; @@ -143,8 +148,14 @@ int main(int argc, char** argv) return 1; } const int len = strlen(fn); - if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) { - png = true; + if (len >= 4) { + if (0 == strcmp(fn+len-4, ".png")) { + png = true; + } else if (0 == strcmp(fn+len-4, ".jpg")) { + jpeg = true; + } else if (len > 4 && 0 == strcmp(fn+len-5, ".jpeg")) { + jpeg = true; + } } } @@ -220,11 +231,12 @@ int main(int argc, char** argv) } if (base != NULL) { - if (png) { + if (png || jpeg) { const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType); SkAutoTUnref data(SkImageEncoder::EncodeData(info, base, s*bytesPerPixel(f), - SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality)); + (png ? SkImageEncoder::kPNG_Type : SkImageEncoder::kJPEG_Type), + SkImageEncoder::kDefaultQuality)); if (data.get()) { write(fd, data->data(), data->size()); } diff --git a/cmds/settings/Android.mk b/cmds/settings/Android.mk index 05deb99f72283..c39753595809c 100644 --- a/cmds/settings/Android.mk +++ b/cmds/settings/Android.mk @@ -4,6 +4,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_STATIC_JAVA_LIBRARIES := org.cyanogenmod.platform.internal LOCAL_MODULE := settings LOCAL_MODULE_TAGS := optional include $(BUILD_JAVA_LIBRARY) diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java index c27d0c099b911..64610fe6f2ddd 100644 --- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java +++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java @@ -29,6 +29,8 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; +import cyanogenmod.providers.CMSettings; import java.util.ArrayList; import java.util.Collections; @@ -36,6 +38,9 @@ public final class SettingsCmd { + private static final String SETTINGS_AUTHORITY = Settings.AUTHORITY; + private static final String CMSETTINGS_AUTHORITY = CMSettings.AUTHORITY; + enum CommandVerb { UNSPECIFIED, GET, @@ -51,6 +56,7 @@ enum CommandVerb { String mTable = null; String mKey = null; String mValue = null; + boolean mUseCMSettingsProvider = false; public static void main(String[] args) { if (args == null || args.length < 2) { @@ -77,6 +83,8 @@ public void run() { break; } mUser = Integer.parseInt(nextArg()); + } else if ("--cm".equals(arg)) { + mUseCMSettingsProvider = true; } else if (mVerb == CommandVerb.UNSPECIFIED) { if ("get".equalsIgnoreCase(arg)) { mVerb = CommandVerb.GET; @@ -133,13 +141,21 @@ public void run() { mUser = UserHandle.USER_OWNER; } + // Implicitly use CMSettings provider if the setting is a legacy setting + if (!mUseCMSettingsProvider && isLegacySetting(mTable, mKey)) { + System.err.println("'" + mKey + "' has moved to CMSettings. Use --cm to avoid " + + "this warning in the future."); + mUseCMSettingsProvider = true; + } + try { IActivityManager activityManager = ActivityManagerNative.getDefault(); IContentProvider provider = null; IBinder token = new Binder(); try { ContentProviderHolder holder = activityManager.getContentProviderExternal( - "settings", UserHandle.USER_OWNER, token); + mUseCMSettingsProvider ? CMSETTINGS_AUTHORITY : SETTINGS_AUTHORITY, + UserHandle.USER_OWNER, token); if (holder == null) { throw new IllegalStateException("Could not find settings provider"); } @@ -182,9 +198,15 @@ public void run() { } private List listForUser(IContentProvider provider, int userHandle, String table) { - final Uri uri = "system".equals(table) ? Settings.System.CONTENT_URI - : "secure".equals(table) ? Settings.Secure.CONTENT_URI - : "global".equals(table) ? Settings.Global.CONTENT_URI + final Uri systemUri = mUseCMSettingsProvider ? CMSettings.System.CONTENT_URI + : Settings.System.CONTENT_URI; + final Uri secureUri = mUseCMSettingsProvider ? CMSettings.Secure.CONTENT_URI + : Settings.Secure.CONTENT_URI; + final Uri globalUri = mUseCMSettingsProvider ? CMSettings.Global.CONTENT_URI + : Settings.Global.CONTENT_URI; + final Uri uri = "system".equals(table) ? systemUri + : "secure".equals(table) ? secureUri + : "global".equals(table) ? globalUri : null; final ArrayList lines = new ArrayList(); if (uri == null) { @@ -220,10 +242,16 @@ private String nextArg() { String getForUser(IContentProvider provider, int userHandle, final String table, final String key) { + final String systemGetCommand = mUseCMSettingsProvider ? CMSettings.CALL_METHOD_GET_SYSTEM + : Settings.CALL_METHOD_GET_SYSTEM; + final String secureGetCommand = mUseCMSettingsProvider ? CMSettings.CALL_METHOD_GET_SECURE + : Settings.CALL_METHOD_GET_SECURE; + final String globalGetCommand = mUseCMSettingsProvider ? CMSettings.CALL_METHOD_GET_GLOBAL + : Settings.CALL_METHOD_GET_GLOBAL; final String callGetCommand; - if ("system".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SYSTEM; - else if ("secure".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SECURE; - else if ("global".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_GLOBAL; + if ("system".equals(table)) callGetCommand = systemGetCommand; + else if ("secure".equals(table)) callGetCommand = secureGetCommand; + else if ("global".equals(table)) callGetCommand = globalGetCommand; else { System.err.println("Invalid table; no put performed"); throw new IllegalArgumentException("Invalid table " + table); @@ -232,7 +260,8 @@ String getForUser(IContentProvider provider, int userHandle, String result = null; try { Bundle arg = new Bundle(); - arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); + arg.putInt(mUseCMSettingsProvider ? CMSettings.CALL_METHOD_USER_KEY + : Settings.CALL_METHOD_USER_KEY, userHandle); Bundle b = provider.call(resolveCallingPackage(), callGetCommand, key, arg); if (b != null) { result = b.getPairValue(); @@ -245,10 +274,16 @@ String getForUser(IContentProvider provider, int userHandle, void putForUser(IContentProvider provider, int userHandle, final String table, final String key, final String value) { + final String systemPutCommand = mUseCMSettingsProvider ? CMSettings.CALL_METHOD_PUT_SYSTEM + : Settings.CALL_METHOD_PUT_SYSTEM; + final String securePutCommand = mUseCMSettingsProvider ? CMSettings.CALL_METHOD_PUT_SECURE + : Settings.CALL_METHOD_PUT_SECURE; + final String globalPutCommand = mUseCMSettingsProvider ? CMSettings.CALL_METHOD_PUT_GLOBAL + : Settings.CALL_METHOD_PUT_GLOBAL; final String callPutCommand; - if ("system".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM; - else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE; - else if ("global".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_GLOBAL; + if ("system".equals(table)) callPutCommand = systemPutCommand; + else if ("secure".equals(table)) callPutCommand = securePutCommand; + else if ("global".equals(table)) callPutCommand = globalPutCommand; else { System.err.println("Invalid table; no put performed"); return; @@ -257,7 +292,8 @@ void putForUser(IContentProvider provider, int userHandle, try { Bundle arg = new Bundle(); arg.putString(Settings.NameValueTable.VALUE, value); - arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); + arg.putInt(mUseCMSettingsProvider ? CMSettings.CALL_METHOD_USER_KEY + : Settings.CALL_METHOD_USER_KEY, userHandle); provider.call(resolveCallingPackage(), callPutCommand, key, arg); } catch (RemoteException e) { System.err.println("Can't set key " + key + " in " + table + " for user " + userHandle); @@ -266,10 +302,16 @@ void putForUser(IContentProvider provider, int userHandle, int deleteForUser(IContentProvider provider, int userHandle, final String table, final String key) { + final Uri systemUri = mUseCMSettingsProvider ? CMSettings.System.getUriFor(key) + : Settings.System.getUriFor(key); + final Uri secureUri = mUseCMSettingsProvider ? CMSettings.Secure.getUriFor(key) + : Settings.Secure.getUriFor(key); + final Uri globalUri = mUseCMSettingsProvider ? CMSettings.Global.getUriFor(key) + : Settings.Global.getUriFor(key); Uri targetUri; - if ("system".equals(table)) targetUri = Settings.System.getUriFor(key); - else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key); - else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key); + if ("system".equals(table)) targetUri = systemUri; + else if ("secure".equals(table)) targetUri = secureUri; + else if ("global".equals(table)) targetUri = globalUri; else { System.err.println("Invalid table; no delete performed"); throw new IllegalArgumentException("Invalid table " + table); @@ -286,12 +328,26 @@ int deleteForUser(IContentProvider provider, int userHandle, } private static void printUsage() { - System.err.println("usage: settings [--user NUM] get namespace key"); - System.err.println(" settings [--user NUM] put namespace key value"); - System.err.println(" settings [--user NUM] delete namespace key"); - System.err.println(" settings [--user NUM] list namespace"); + System.err.println("usage: settings [--user NUM] [--cm] get namespace key"); + System.err.println(" settings [--user NUM] [--cm] put namespace key value"); + System.err.println(" settings [--user NUM] [--cm] delete namespace key"); + System.err.println(" settings [--user NUM] [--cm] list namespace"); System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive"); System.err.println("If '--user NUM' is not given, the operations are performed on the owner user."); + System.err.println("If '--cm' is given, the operations are performed on the CMSettings provider."); + } + + private static boolean isLegacySetting(String table, String key) { + if (!TextUtils.isEmpty(key)) { + if ("system".equals(table)) { + return CMSettings.System.isLegacySetting(key); + } else if ("secure".equals(table)) { + return CMSettings.Secure.isLegacySetting(key); + } else if ("global".equals(table)) { + return CMSettings.Global.isLegacySetting(key); + } + } + return false; } public static String resolveCallingPackage() { diff --git a/cmds/tm/Android.mk b/cmds/tm/Android.mk new file mode 100644 index 0000000000000..97e8ee44ac250 --- /dev/null +++ b/cmds/tm/Android.mk @@ -0,0 +1,16 @@ +# Copyright 2015 The CyanogenMod Project +# +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_STATIC_JAVA_LIBRARIES := org.cyanogenmod.platform.internal +LOCAL_MODULE := tm +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := tm +LOCAL_SRC_FILES := tm +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +include $(BUILD_PREBUILT) diff --git a/cmds/tm/MODULE_LICENSE_APACHE2 b/cmds/tm/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/cmds/tm/NOTICE b/cmds/tm/NOTICE new file mode 100644 index 0000000000000..0820f6d9e2130 --- /dev/null +++ b/cmds/tm/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2015, The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/tm/src/com/android/commands/tm/Tm.java b/cmds/tm/src/com/android/commands/tm/Tm.java new file mode 100644 index 0000000000000..0ba5cb982c4d9 --- /dev/null +++ b/cmds/tm/src/com/android/commands/tm/Tm.java @@ -0,0 +1,204 @@ +/* +** +** Copyright 2015, The CyanogenMod 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.commands.tm; + +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.ParceledListSlice; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.AndroidException; +import com.android.internal.os.BaseCommand; + +import cyanogenmod.app.CMContextConstants; +import cyanogenmod.themes.IThemeService; +import cyanogenmod.themes.ThemeChangeRequest; + +import org.cyanogenmod.internal.util.ThemeUtils; + +import java.io.PrintStream; +import java.util.List; +import java.util.Map; + +public class Tm extends BaseCommand { + private static final String SYSTEM_THEME = "system"; + + IThemeService mTs; + IPackageManager mPm; + + /** + * Command-line entry point. + * + * @param args The command-line arguments + */ + public static void main(String[] args) { + (new Tm()).run(args); + } + + public void onShowUsage(PrintStream out) { + List components = ThemeUtils.getAllComponents(); + StringBuilder sb = new StringBuilder(); + sb.append("usage: tm [subcommand] [options]\n"); + sb.append(" tm list\n"); + sb.append(" tm apply [-r] [-c [-c ] ...]\n"); + sb.append(" tm rebuild\n"); + sb.append(" tm process \n"); + sb.append("\n"); + sb.append("tm list: return a list of theme packages.\n"); + sb.append("\n"); + sb.append("tm apply: applies the components for the theme specified by PACKAGE_NAME.\n"); + sb.append(" -r: remove per app themes\n"); + sb.append(" [-c [-c ] ...]\n"); + sb.append(" if no components are specified all components will be applied.\n"); + sb.append(" Valid components are:\n"); + for (String component : components) { + sb.append(" "); + sb.append(component); + sb.append("\n"); + } + sb.append("\n"); + sb.append("tm rebuild: rebuilds the resource cache.\n"); + sb.append("\n"); + sb.append("tm process: processes the theme resources for the theme specified by " + + "PACKAGE_NAME.\n"); + + out.println(sb.toString()); + } + + public void onRun() throws Exception { + mTs = IThemeService.Stub.asInterface(ServiceManager + .getService(CMContextConstants.CM_THEME_SERVICE)); + if (mTs == null) { + System.err.println(NO_SYSTEM_ERROR_CODE); + throw new AndroidException("Can't connect to theme service; is the system running?"); + } + + mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); + if (mPm == null) { + System.err.println(NO_SYSTEM_ERROR_CODE); + throw new AndroidException("Can't connect to package manager; is the system running?"); + } + + String op = nextArgRequired(); + + if (op.equals("list")) { + runListThemePackages(); + } else if (op.equals("apply")) { + runApplyTheme(); + } else if (op.equals("rebuild")) { + runRebuildResourceCache(); + } else if (op.equals("process")) { + runProcessTheme(); + } else { + showError("Error: unknown command '" + op + "'"); + return; + } + } + + private void runListThemePackages() throws Exception { + List packages = getInstalledPackages(mPm, 0, UserHandle.USER_OWNER); + + // there is always a "system" theme available + System.out.println("package:system [theme]"); + for (PackageInfo info : packages) { + if (info.isThemeApk || info.isLegacyIconPackApk) { + System.out.print("package:"); + System.out.print(info.packageName); + if (info.isThemeApk) { + System.out.println(" [theme]"); + } else { + System.out.println(" [icon pack]"); + } + } + } + } + + private void runApplyTheme() throws Exception { + String pkgName = nextArg(); + if (pkgName == null) { + System.err.println("Error: didn't specify theme package to apply"); + return; + } + if (!SYSTEM_THEME.equals(pkgName)) { + PackageInfo info = mPm.getPackageInfo(pkgName, 0, UserHandle.USER_OWNER); + if (info == null) { + System.err.println("Error: invalid package name"); + return; + } + if (!(info.isThemeApk || info.isLegacyIconPackApk)) { + System.err.println("Error: package is not a theme or icon pack"); + return; + } + } + + boolean removePerAppThemes = false; + + ThemeChangeRequest.Builder builder = new ThemeChangeRequest.Builder(); + String opt; + while ((opt=nextOption()) != null) { + if (opt.equals("-c")) { + builder.setComponent(nextArgRequired(), pkgName); + } else if (opt.equals("-r")) { + removePerAppThemes = true; + } + } + + // No components specified so let's just try and apply EVERYTHING! + Map componentMap = builder.build().getThemeComponentsMap(); + if (componentMap.size() == 0) { + List components = ThemeUtils.getAllComponents(); + for (String component : components) { + builder.setComponent(component, pkgName); + } + } + mTs.requestThemeChange(builder.build(), removePerAppThemes); + } + + private void runRebuildResourceCache() throws Exception { + mTs.rebuildResourceCache(); + } + + private void runProcessTheme() throws Exception { + String pkgName = nextArg(); + if (pkgName == null) { + System.err.println("Error: didn't specify theme package to apply"); + return; + } + PackageInfo info = mPm.getPackageInfo(pkgName, 0, UserHandle.USER_OWNER); + if (info == null) { + System.err.println("Error: invalid package name"); + return; + } + if (!info.isThemeApk) { + System.err.println("Error: package is not a theme"); + return; + } + + mTs.processThemeResources(pkgName); + } + + @SuppressWarnings("unchecked") + private List getInstalledPackages(IPackageManager pm, int flags, int userId) + throws RemoteException { + ParceledListSlice slice = pm.getInstalledPackages(flags, userId); + return slice.getList(); + } + +} diff --git a/cmds/tm/tm b/cmds/tm/tm new file mode 100755 index 0000000000000..dc95b6ffc59f1 --- /dev/null +++ b/cmds/tm/tm @@ -0,0 +1,6 @@ +# Script to start "tm" on the device, which has a very rudimentary +# shell. +# +base=/system +export CLASSPATH=$base/framework/tm.jar +exec app_process $base/bin com.android.commands.tm.Tm "$@" diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 9ef13de6d4443..eb59f6bd03718 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -329,6 +329,7 @@ public static AccountManager get(Context context) { * @return The account's password, null if none or if the account doesn't exist */ public String getPassword(final Account account) { + android.util.SeempLog.record(22); if (account == null) throw new IllegalArgumentException("account is null"); try { return mService.getPassword(account); @@ -357,6 +358,7 @@ public String getPassword(final Account account) { * @return The user data, null if the account or key doesn't exist */ public String getUserData(final Account account, final String key) { + android.util.SeempLog.record(23); if (account == null) throw new IllegalArgumentException("account is null"); if (key == null) throw new IllegalArgumentException("key is null"); try { @@ -567,6 +569,7 @@ public AccountManagerFuture getAuthTokenLabel( if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); return new Future2Task(handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.getAuthTokenLabel(mResponse, accountType, authTokenType); } @@ -612,6 +615,7 @@ public AccountManagerFuture hasFeatures(final Account account, if (features == null) throw new IllegalArgumentException("features is null"); return new Future2Task(handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.hasFeatures(mResponse, account, features, mContext.getOpPackageName()); } public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { @@ -664,6 +668,7 @@ public AccountManagerFuture getAccountsByTypeAndFeatures( if (type == null) throw new IllegalArgumentException("type is null"); return new Future2Task(handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.getAccountsByFeatures(mResponse, type, features, mContext.getOpPackageName()); } @@ -707,6 +712,7 @@ public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException { * already exists, the account is null, or another error occurs. */ public boolean addAccountExplicitly(Account account, String password, Bundle userdata) { + android.util.SeempLog.record(24); if (account == null) throw new IllegalArgumentException("account is null"); try { return mService.addAccountExplicitly(account, password, userdata); @@ -777,6 +783,7 @@ public AccountManagerFuture renameAccount( return new Future2Task(handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.renameAccount(mResponse, account, newName); } @Override @@ -837,10 +844,12 @@ public String getPreviousName(final Account account) { @Deprecated public AccountManagerFuture removeAccount(final Account account, AccountManagerCallback callback, Handler handler) { + android.util.SeempLog.record(25); if (account == null) throw new IllegalArgumentException("account is null"); return new Future2Task(handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.removeAccount(mResponse, account, false); } @Override @@ -896,10 +905,12 @@ public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { */ public AccountManagerFuture removeAccount(final Account account, final Activity activity, AccountManagerCallback callback, Handler handler) { + android.util.SeempLog.record(28); if (account == null) throw new IllegalArgumentException("account is null"); return new AmsTask(activity, handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(34); mService.removeAccount(mResponse, account, activity != null); } }.start(); @@ -921,6 +932,7 @@ public AccountManagerFuture removeAccountAsUser(final Account account, return new Future2Task(handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.removeAccountAsUser(mResponse, account, false, userHandle.getIdentifier()); } @Override @@ -946,6 +958,7 @@ public AccountManagerFuture removeAccountAsUser(final Account account, throw new IllegalArgumentException("userHandle is null"); return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(34); mService.removeAccountAsUser(mResponse, account, activity != null, userHandle.getIdentifier()); } @@ -1062,6 +1075,7 @@ public String peekAuthToken(final Account account, final String authTokenType) { * @param password The password to set, null to clear the password */ public void setPassword(final Account account, final String password) { + android.util.SeempLog.record(26); if (account == null) throw new IllegalArgumentException("account is null"); try { mService.setPassword(account, password); @@ -1091,6 +1105,7 @@ public void setPassword(final Account account, final String password) { * @param account The account whose password to clear */ public void clearPassword(final Account account) { + android.util.SeempLog.record(27); if (account == null) throw new IllegalArgumentException("account is null"); try { mService.clearPassword(account); @@ -1119,6 +1134,7 @@ public void clearPassword(final Account account) { * @param value String value to set, {@code null} to clear this user data key */ public void setUserData(final Account account, final String key, final String value) { + android.util.SeempLog.record(28); if (account == null) throw new IllegalArgumentException("account is null"); if (key == null) throw new IllegalArgumentException("key is null"); try { @@ -1270,6 +1286,7 @@ public AccountManagerFuture getAuthToken( optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.getAuthToken(mResponse, account, authTokenType, false /* notifyOnAuthFailure */, true /* expectActivityLaunch */, optionsIn); @@ -1438,6 +1455,7 @@ public AccountManagerFuture getAuthToken( optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); return new AmsTask(null, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.getAuthToken(mResponse, account, authTokenType, notifyAuthFailure, false /* expectActivityLaunch */, optionsIn); } @@ -1498,6 +1516,7 @@ public AccountManagerFuture addAccount(final String accountType, final String authTokenType, final String[] requiredFeatures, final Bundle addAccountOptions, final Activity activity, AccountManagerCallback callback, Handler handler) { + android.util.SeempLog.record(29); if (accountType == null) throw new IllegalArgumentException("accountType is null"); final Bundle optionsIn = new Bundle(); if (addAccountOptions != null) { @@ -1507,6 +1526,7 @@ public AccountManagerFuture addAccount(final String accountType, return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.addAccount(mResponse, accountType, authTokenType, requiredFeatures, activity != null, optionsIn); } @@ -1531,6 +1551,7 @@ public AccountManagerFuture addAccountAsUser(final String accountType, return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.addAccountAsUser(mResponse, accountType, authTokenType, requiredFeatures, activity != null, optionsIn, userHandle.getIdentifier()); } @@ -1578,6 +1599,7 @@ public AccountManagerFuture copyAccountToUser( return new Future2Task(handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(34); mService.copyAccountToUser( mResponse, account, UserHandle.USER_OWNER, user.getIdentifier()); } @@ -1705,6 +1727,7 @@ public AccountManagerFuture confirmCredentialsAsUser(final Account accou final int userId = userHandle.getIdentifier(); return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.confirmCredentialsAsUser(mResponse, account, options, activity != null, userId); } @@ -1817,9 +1840,11 @@ public void doWork() throws RemoteException { public AccountManagerFuture editProperties(final String accountType, final Activity activity, final AccountManagerCallback callback, final Handler handler) { + android.util.SeempLog.record(30); if (accountType == null) throw new IllegalArgumentException("accountType is null"); return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.editProperties(mResponse, accountType, activity != null); } }.start(); @@ -2175,6 +2200,7 @@ private class GetAuthTokenByTypeAndFeaturesTask private volatile int mNumAccounts = 0; public void doWork() throws RemoteException { + android.util.SeempLog.record(31); getAccountsByTypeAndFeatures(mAccountType, mFeatures, new AccountManagerCallback() { public void run(AccountManagerFuture future) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 87c9efc2e1041..0ae918762e1b7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -41,6 +41,7 @@ import android.content.pm.IPackageDataObserver; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Color; @@ -59,6 +60,7 @@ import android.util.DisplayMetrics; import android.util.Size; import android.util.Slog; + import org.xmlpull.v1.XmlSerializer; import java.io.FileDescriptor; @@ -525,8 +527,16 @@ public static boolean isLowRamDeviceStatic() { * @hide */ static public boolean isHighEndGfx() { - return !isLowRamDeviceStatic() && - !Resources.getSystem().getBoolean(com.android.internal.R.bool.config_avoidGfxAccel); + return (!isLowRamDeviceStatic() && + !Resources.getSystem().getBoolean(com.android.internal.R.bool.config_avoidGfxAccel)) + || isForcedHighEndGfx(); + } + + /** + * @hide + */ + public static boolean isForcedHighEndGfx() { + return SystemProperties.getBoolean("persist.sys.force_highendgfx", false); } /** @@ -1315,6 +1325,23 @@ public List getRunningTasks(int maxNum) } } + /** + * Check whether the current foreground tasks belongs to a given package. + * + * @param packageName Name of the package to check for + * + * @return Whether the current foreground tasks belongs to the given package + * @hide + */ + public boolean isPackageInForeground(String packageName) { + try { + return ActivityManagerNative.getDefault().isPackageInForeground(packageName); + } catch (RemoteException e) { + // System dead, we will be dead too soon! + return false; + } + } + /** * Completely remove the given task. * @@ -2298,6 +2325,16 @@ public List getRunningExternalApplications() { return null; } } + /** + * @hide + */ + public Configuration getConfiguration() { + try { + return ActivityManagerNative.getDefault().getConfiguration(); + } catch (RemoteException e) { + return null; + } + } /** * Sets the memory trim mode for a process and schedules a memory trim operation. @@ -2988,4 +3025,17 @@ public void setExcludeFromRecents(boolean exclude) { } } } + + /** + * @throws SecurityException Throws SecurityException if the caller does + * not hold the {@link android.Manifest.permission#CHANGE_CONFIGURATION} permission. + * + * @hide + */ + public void updateConfiguration(Configuration values) throws SecurityException { + try { + ActivityManagerNative.getDefault().updateConfiguration(values); + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index f6e0735a2b0f2..544331fb6b405 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -577,6 +577,15 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags) return true; } + case GET_CALLING_PACKAGE_FOR_BROADCAST_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + boolean foreground = data.readInt() == 1 ? true : false; + String res = getCallingPackageForBroadcast(foreground); + reply.writeNoException(); + reply.writeString(res); + return true; + } + case GET_CALLING_ACTIVITY_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -638,6 +647,15 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags) return true; } + case IS_PACKAGE_IN_FOREGROUND_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String packageName = data.readString(); + boolean result = isPackageInForeground(packageName); + reply.writeNoException(); + reply.writeInt(result ? 1 : 0); + return true; + } + case GET_RECENT_TASKS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int maxNum = data.readInt(); @@ -1582,9 +1600,10 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags) case START_BACKUP_AGENT_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data); + String packageName = data.readString(); int backupRestoreMode = data.readInt(); - boolean success = bindBackupAgent(info, backupRestoreMode); + int userId = data.readInt(); + boolean success = bindBackupAgent(packageName, backupRestoreMode, userId); reply.writeNoException(); reply.writeInt(success ? 1 : 0); return true; @@ -2078,11 +2097,14 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags) return true; } - case SHOW_BOOT_MESSAGE_TRANSACTION: { + case UPDATE_BOOT_PROGRESS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - CharSequence msg = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data); + int stage = data.readInt(); + ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data); + int current = data.readInt(); + int total = data.readInt(); boolean always = data.readInt() != 0; - showBootMessage(msg, always); + updateBootProgress(stage, info, current, total, always); reply.writeNoException(); return true; } @@ -2096,7 +2118,7 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags) case KEYGUARD_GOING_AWAY_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - keyguardGoingAway(data.readInt() != 0, data.readInt() != 0); + keyguardGoingAway(data.readInt() != 0, data.readInt() != 0, data.readInt() != 0); reply.writeNoException(); return true; } @@ -3192,6 +3214,19 @@ public String getCallingPackage(IBinder token) throws RemoteException reply.recycle(); return res; } + public String getCallingPackageForBroadcast(boolean foreground) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(foreground ? 1 : 0); + mRemote.transact(GET_CALLING_PACKAGE_FOR_BROADCAST_TRANSACTION, data, reply, 0); + reply.readException(); + String res = reply.readString(); + data.recycle(); + reply.recycle(); + return res; + } public ComponentName getCallingActivity(IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); @@ -3278,6 +3313,18 @@ public List getTasks(int maxNum, int flags) reply.recycle(); return list; } + public boolean isPackageInForeground(String packageName) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(packageName); + mRemote.transact(IS_PACKAGE_IN_FOREGROUND_TRANSACTION, data, reply, 0); + reply.readException(); + boolean result = reply.readInt() != 0; + data.recycle(); + reply.recycle(); + return result; + } public List getRecentTasks(int maxNum, int flags, int userId) throws RemoteException { Parcel data = Parcel.obtain(); @@ -3831,13 +3878,14 @@ public IBinder peekService(Intent service, String resolvedType, String callingPa return binder; } - public boolean bindBackupAgent(ApplicationInfo app, int backupRestoreMode) + public boolean bindBackupAgent(String packageName, int backupRestoreMode, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); - app.writeToParcel(data, 0); + data.writeString(packageName); data.writeInt(backupRestoreMode); + data.writeInt(userId); mRemote.transact(START_BACKUP_AGENT_TRANSACTION, data, reply, 0); reply.readException(); boolean success = reply.readInt() != 0; @@ -5240,13 +5288,17 @@ public long[] getProcessPss(int[] pids) throws RemoteException { return res; } - public void showBootMessage(CharSequence msg, boolean always) throws RemoteException { + public void updateBootProgress(int stage, ApplicationInfo optimizedApp, + int currentAppPos, int totalAppCount, boolean always) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); - TextUtils.writeToParcel(msg, data, 0); + data.writeInt(stage); + optimizedApp.writeToParcel(data, 0); + data.writeInt(currentAppPos); + data.writeInt(totalAppCount); data.writeInt(always ? 1 : 0); - mRemote.transact(SHOW_BOOT_MESSAGE_TRANSACTION, data, reply, 0); + mRemote.transact(UPDATE_BOOT_PROGRESS_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); @@ -5263,12 +5315,14 @@ public void keyguardWaitingForActivityDrawn() throws RemoteException { } public void keyguardGoingAway(boolean disableWindowAnimations, - boolean keyguardGoingToNotificationShade) throws RemoteException { + boolean keyguardGoingToNotificationShade, + boolean keyguardShowingMedia) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(disableWindowAnimations ? 1 : 0); data.writeInt(keyguardGoingToNotificationShade ? 1 : 0); + data.writeInt(keyguardShowingMedia ? 1 : 0); mRemote.transact(KEYGUARD_GOING_AWAY_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index fd88a0549a101..782dc462726b7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -46,6 +46,7 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; import android.net.ConnectivityManager; import android.net.IConnectivityManager; @@ -78,6 +79,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.security.NetworkSecurityPolicy; +import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -90,6 +92,7 @@ import android.util.SuperNotCalledException; import android.view.Display; import android.view.HardwareRenderer; +import android.view.InflateException; import android.view.View; import android.view.ViewDebug; import android.view.ViewManager; @@ -893,9 +896,25 @@ public void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args) public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky, int sendingUser, int processState) throws RemoteException { - updateProcessState(processState, false); - receiver.performReceive(intent, resultCode, dataStr, extras, ordered, - sticky, sendingUser); + RemoteException remoteException = null; + if (!Binder.isProxy(receiver)) { + updateProcessState(processState, false); + try { + receiver.performReceive(intent, resultCode, dataStr, extras, ordered, + sticky, sendingUser); + return; + } catch (RemoteException e) { + remoteException = e; + } + } + if (ordered) { + Slog.w(TAG, receiver + " is no longer alive"); + ActivityManagerNative.getDefault().finishReceiver(receiver.asBinder(), + resultCode, dataStr, extras, true, intent.getFlags()); + if (remoteException != null) { + throw remoteException; + } + } } @Override @@ -1697,9 +1716,21 @@ Configuration applyConfigCompatMainThread(int displayDensity, Configuration conf */ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, String[] libDirs, int displayId, Configuration overrideConfiguration, - LoadedApk pkgInfo) { + LoadedApk pkgInfo, Context context, String pkgName) { return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs, - displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo()); + displayId, pkgName, overrideConfiguration, pkgInfo.getCompatibilityInfo(), context, + pkgInfo.getApplicationInfo().isThemeable); + } + + /** + * Creates the top level resources for the given package. + */ + Resources getTopLevelThemedResources(String resDir, int displayId, + Configuration overrideConfiguration, LoadedApk pkgInfo, + String pkgName, String themePkgName) { + return mResourcesManager.getTopLevelThemedResources(resDir, displayId, pkgName, + themePkgName, pkgInfo.getCompatibilityInfo(), + pkgInfo.getApplicationInfo().isThemeable); } final Handler getHandler() { @@ -1817,8 +1848,7 @@ private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compat } LoadedApk packageInfo = ref != null ? ref.get() : null; - if (packageInfo == null || (packageInfo.mResources != null - && !packageInfo.mResources.getAssets().isUpToDate())) { + if (packageInfo == null) { if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package " : "Loading resource-only package ") + aInfo.packageName + " (in " + (mBoundApplication != null @@ -1844,6 +1874,10 @@ private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compat new WeakReference(packageInfo)); } } + if (packageInfo.mResources != null + && !packageInfo.mResources.getAssets().isUpToDate()) { + packageInfo.mResources = null; + } return packageInfo; } } @@ -4258,8 +4292,12 @@ static void freeTextLayoutCachesIfNeeded(int configDiff) { if (configDiff != 0) { // Ask text layout engine to free its caches if there is a locale change boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0); - if (hasLocaleConfigChange) { + boolean hasFontConfigChange = ((configDiff & ActivityInfo.CONFIG_THEME_FONT) != 0); + if (hasLocaleConfigChange || hasFontConfigChange) { Canvas.freeTextLayoutCaches(); + if (hasFontConfigChange) { + Typeface.recreateDefaults(); + } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Cleared TextLayout Caches"); } } @@ -4428,7 +4466,7 @@ private void updateDefaultDensity() { + DisplayMetrics.DENSITY_DEVICE + " to " + mCurDefaultDisplayDpi); DisplayMetrics.DENSITY_DEVICE = mCurDefaultDisplayDpi; - Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT); + Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEVICE); } } @@ -4528,7 +4566,8 @@ private void handleBindApplication(AppBindData data) { } - final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24)); + final boolean is24Hr = android.text.format.DateFormat.is24HourFormat( + mCoreSettings.getString(Settings.System.TIME_12_24), data.config.locale); DateFormat.set24HourTimePref(is24Hr); View.mDebugViewAttributes = diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 330d730b89cba..b0afe7c2a75c8 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -100,6 +100,14 @@ public class AlarmManager { */ public static final int ELAPSED_REALTIME = 3; + /** @hide + * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()} + * (wall clock time in UTC), which will wake up the device when + * it goes off. And it will power on the devices when it shuts down. + * Set as 5 to make it be compatible with android_alarm_type. + */ + public static final int RTC_POWEROFF_WAKEUP = 5; + /** * Broadcast Action: Sent after the value returned by * {@link #getNextAlarmClock()} has changed. diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 09c0a6e3ae462..11a5ee85f0865 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +31,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.SystemProperties; import android.os.UserManager; import android.util.ArrayMap; @@ -69,6 +73,10 @@ public class AppOpsManager { * will do this for you). */ + /** {@hide */ + public static final String ACTION_SU_SESSION_CHANGED = + "android.intent.action.SU_SESSION_CHANGED"; + final Context mContext; final IAppOpsService mService; final ArrayMap mModeWatchers @@ -103,9 +111,18 @@ public class AppOpsManager { */ public static final int MODE_DEFAULT = 3; + /** + * @hide Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: + * AppOps Service should show a dialog box on screen to get user + * permission. + */ + public static final int MODE_ASK = 4; + // when adding one of these: // - increment _NUM_OP - // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode + // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDefaultStrictMode, + // sOpToOpString, sOpStrictMode. + // - add descriptive strings to frameworks/base/core/res/res/values/config.xml // - add descriptive strings to Settings/res/values/arrays.xml // - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app) @@ -237,8 +254,20 @@ public class AppOpsManager { public static final int OP_TURN_SCREEN_ON = 61; /** @hide Get device accounts. */ public static final int OP_GET_ACCOUNTS = 62; + /** @hide Wifi state change **/ + public static final int OP_WIFI_CHANGE = 63; + /** @hide */ + public static final int OP_BLUETOOTH_CHANGE = 64; /** @hide */ - public static final int _NUM_OP = 63; + public static final int OP_BOOT_COMPLETED = 65; + /** @hide */ + public static final int OP_NFC_CHANGE = 66; + /** @hide */ + public static final int OP_DATA_CONNECT_CHANGE = 67; + /** @hide */ + public static final int OP_SU = 68; + /** @hide */ + public static final int _NUM_OP = 69; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -336,6 +365,19 @@ public class AppOpsManager { /** @hide Get device accounts. */ public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts"; + /** @hide **/ + private static final String OPSTR_WIFI_CHANGE = + "android:wifi_change"; + private static final String OPSTR_BLUETOOTH_CHANGE = + "android:bluetooth_change"; + private static final String OPSTR_BOOT_COMPLETED = + "android:boot_completed"; + private static final String OPSTR_NFC_CHANGE = + "android:nfc_change"; + private static final String OPSTR_DATA_CONNECT_CHANGE = + "android:data_connect_change"; + private static final String OPSTR_SU = + "android:su"; /** * This maps each operation to the operation that serves as the @@ -356,7 +398,7 @@ public class AppOpsManager { OP_WRITE_CALL_LOG, OP_READ_CALENDAR, OP_WRITE_CALENDAR, - OP_COARSE_LOCATION, + OP_WIFI_SCAN, OP_POST_NOTIFICATION, OP_COARSE_LOCATION, OP_CALL_PHONE, @@ -409,6 +451,12 @@ public class AppOpsManager { OP_WRITE_EXTERNAL_STORAGE, OP_TURN_SCREEN_ON, OP_GET_ACCOUNTS, + OP_WIFI_CHANGE, + OP_BLUETOOTH_CHANGE, + OP_BOOT_COMPLETED, + OP_NFC_CHANGE, + OP_DATA_CONNECT_CHANGE, + OP_SU }; /** @@ -478,7 +526,13 @@ public class AppOpsManager { OPSTR_READ_EXTERNAL_STORAGE, OPSTR_WRITE_EXTERNAL_STORAGE, null, - OPSTR_GET_ACCOUNTS + OPSTR_GET_ACCOUNTS, + OPSTR_WIFI_CHANGE, + OPSTR_BLUETOOTH_CHANGE, + OPSTR_BOOT_COMPLETED, + OPSTR_NFC_CHANGE, + OPSTR_DATA_CONNECT_CHANGE, + OPSTR_SU, }; /** @@ -549,6 +603,12 @@ public class AppOpsManager { "WRITE_EXTERNAL_STORAGE", "TURN_ON_SCREEN", "GET_ACCOUNTS", + "WIFI_CHANGE", + "BLUETOOTH_CHANGE", + "BOOT_COMPLETED", + "NFC_CHANGE", + "DATA_CONNECT_CHANGE", + "SU", }; /** @@ -566,7 +626,7 @@ public class AppOpsManager { android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.READ_CALENDAR, android.Manifest.permission.WRITE_CALENDAR, - android.Manifest.permission.ACCESS_WIFI_STATE, + null, // no permission for wifi scan available null, // no permission required for notifications null, // neighboring cells shares the coarse location perm android.Manifest.permission.CALL_PHONE, @@ -618,7 +678,13 @@ public class AppOpsManager { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, null, // no permission for turning the screen on - Manifest.permission.GET_ACCOUNTS + Manifest.permission.GET_ACCOUNTS, + Manifest.permission.CHANGE_WIFI_STATE, + null, + Manifest.permission.RECEIVE_BOOT_COMPLETED, + Manifest.permission.NFC, + Manifest.permission.MODIFY_PHONE_STATE, + null, }; /** @@ -690,6 +756,12 @@ public class AppOpsManager { null, // WRITE_EXTERNAL_STORAGE null, // TURN_ON_SCREEN null, // GET_ACCOUNTS + null, //WIFI_CHANGE + null, //BLUETOOTH_CHANGE + null, //BOOT_COMPLETED + null, //NFC_CHANGE + null, //DATA_CONNECT_CHANGE + UserManager.DISALLOW_SU, //SU TODO: this should really be investigated. }; /** @@ -760,6 +832,12 @@ public class AppOpsManager { false, // WRITE_EXTERNAL_STORAGE false, // TURN_ON_SCREEN false, // GET_ACCOUNTS + true, // WIFI_CHANGE + true, // BLUETOOTH_CHANGE + true, // BOOT_COMPLETED + true, // NFC_CHANGE + true, //DATA_CONNECT_CHANGE + false, //SU }; /** @@ -829,6 +907,163 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ALLOWED, // OP_WIFI_CHANGE + AppOpsManager.MODE_ALLOWED, // OP_BLUETOOTH_CHANGE + AppOpsManager.MODE_ALLOWED, // OP_BOOT_COMPLETED + AppOpsManager.MODE_ALLOWED, // OP_NFC_CHANGE + AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ASK, // OP_SU + }; + + /** + * This specifies the default mode for each strict operation. + */ + + private static int[] sOpDefaultStrictMode = new int[] { + AppOpsManager.MODE_ASK, // OP_COARSE_LOCATION + AppOpsManager.MODE_ASK, // OP_FINE_LOCATION + AppOpsManager.MODE_ASK, // OP_GPS + AppOpsManager.MODE_ALLOWED, // OP_VIBRATE + AppOpsManager.MODE_ASK, // OP_READ_CONTACTS + AppOpsManager.MODE_ASK, // OP_WRITE_CONTACTS + AppOpsManager.MODE_ASK, // OP_READ_CALL_LOG + AppOpsManager.MODE_ASK, // OP_WRITE_CALL_LOG + AppOpsManager.MODE_ALLOWED, // OP_READ_CALENDAR + AppOpsManager.MODE_ALLOWED, // OP_WRITE_CALENDAR + AppOpsManager.MODE_ASK, // OP_WIFI_SCAN + AppOpsManager.MODE_ALLOWED, // OP_POST_NOTIFICATION + AppOpsManager.MODE_ALLOWED, // OP_NEIGHBORING_CELLS + AppOpsManager.MODE_ASK, // OP_CALL_PHONE + AppOpsManager.MODE_ASK, // OP_READ_SMS + AppOpsManager.MODE_ASK, // OP_WRITE_SMS + AppOpsManager.MODE_ASK, // OP_RECEIVE_SMS + AppOpsManager.MODE_ALLOWED, // OP_RECEIVE_EMERGECY_SMS + AppOpsManager.MODE_ASK, // OP_RECEIVE_MMS + AppOpsManager.MODE_ALLOWED, // OP_RECEIVE_WAP_PUSH + AppOpsManager.MODE_ASK, // OP_SEND_SMS + AppOpsManager.MODE_ALLOWED, // OP_READ_ICC_SMS + AppOpsManager.MODE_ALLOWED, // OP_WRITE_ICC_SMS + AppOpsManager.MODE_ALLOWED, // OP_WRITE_SETTINGS + AppOpsManager.MODE_ALLOWED, // OP_SYSTEM_ALERT_WINDOW + AppOpsManager.MODE_ALLOWED, // OP_ACCESS_NOTIFICATIONS + AppOpsManager.MODE_ASK, // OP_CAMERA + AppOpsManager.MODE_ASK, // OP_RECORD_AUDIO + AppOpsManager.MODE_ALLOWED, // OP_PLAY_AUDIO + AppOpsManager.MODE_ALLOWED, // OP_READ_CLIPBOARD + AppOpsManager.MODE_ALLOWED, // OP_WRITE_CLIPBOARD + AppOpsManager.MODE_ALLOWED, // OP_TAKE_MEDIA_BUTTONS + AppOpsManager.MODE_ALLOWED, // OP_TAKE_AUDIO_FOCUS + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_MASTER_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_VOICE_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_RING_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_MEDIA_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_ALARM_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_NOTIFICATION_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_BLUETOOTH_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_WAKE_LOCK + AppOpsManager.MODE_ALLOWED, // OP_MONITOR_LOCATION + AppOpsManager.MODE_ASK, // OP_MONITOR_HIGH_POWER_LOCATION + AppOpsManager.MODE_DEFAULT, // OP_GET_USAGE_STATS + AppOpsManager.MODE_ALLOWED, // OP_MUTE_MICROPHONE + AppOpsManager.MODE_ALLOWED, // OP_TOAST_WINDOW + AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA + AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN + AppOpsManager.MODE_ALLOWED, // OP WALLPAPER + AppOpsManager.MODE_ALLOWED, // OP_ASSIST_STRUCTURE + AppOpsManager.MODE_ALLOWED, // OP_ASSIST_SCREENSHOT + AppOpsManager.MODE_ALLOWED, // OP_READ_PHONE_STATE + AppOpsManager.MODE_ALLOWED, // OP_ADD_VOICEMAIL + AppOpsManager.MODE_ALLOWED, // OP_USE_SIP + AppOpsManager.MODE_ALLOWED, // OP_PROCESS_OUTGOING_CALLS + AppOpsManager.MODE_ALLOWED, // OP_USE_FINGERPRINT + AppOpsManager.MODE_ALLOWED, // OP_BODY_SENSORS + AppOpsManager.MODE_ALLOWED, // OP_READ_CELL_BROADCASTS + AppOpsManager.MODE_ERRORED, // OP_MOCK_LOCATION + AppOpsManager.MODE_ALLOWED, // OP_READ_EXTERNAL_STORAGE + AppOpsManager.MODE_ALLOWED, // OP_WRITE_EXTERNAL_STORAGE + AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN + AppOpsManager.MODE_ALLOWED, // OP_GET_ACCOUNTS + AppOpsManager.MODE_ASK, // OP_WIFI_CHANGE + AppOpsManager.MODE_ASK, // OP_BLUETOOTH_CHANGE + AppOpsManager.MODE_ALLOWED, // OP_BOOT_COMPLETED + AppOpsManager.MODE_ASK, // OP_NFC_CHANGE + AppOpsManager.MODE_ASK, // OP_DATA_CONNECT_CHANGE + AppOpsManager.MODE_ASK, // OP_SU + }; + + /** + * This specifies if operation is in strict mode. + */ + private final static boolean[] sOpStrictMode = new boolean[] { + true, // OP_COARSE_LOCATION + true, // OP_FINE_LOCATION + true, // OP_GPS + false, // OP_VIBRATE + true, // OP_READ_CONTACTS + true, // OP_WRITE_CONTACTS + true, // OP_READ_CALL_LOG + true, // OP_WRITE_CALL_LOG + false, // OP_READ_CALENDAR + false, // OP_WRITE_CALENDAR + true, // OP_WIFI_SCAN + false, // OP_POST_NOTIFICATION + false, // OP_NEIGHBORING_CELLS + true, // OP_CALL_PHONE + true, // OP_READ_SMS + true, // OP_WRITE_SMS + false, // OP_RECEIVE_SMS + false, // OP_RECEIVE_EMERGECY_SMS + true, // OP_RECEIVE_MMS + false, // OP_RECEIVE_WAP_PUSH + true, // OP_SEND_SMS + false, // OP_READ_ICC_SMS + false, // OP_WRITE_ICC_SMS + false, // OP_WRITE_SETTINGS + false, // OP_SYSTEM_ALERT_WINDOW + false, // OP_ACCESS_NOTIFICATIONS + true, // OP_CAMERA + true, // OP_RECORD_AUDIO + false, // OP_PLAY_AUDIO + false, // OP_READ_CLIPBOARD + false, // OP_WRITE_CLIPBOARD + false, // OP_TAKE_MEDIA_BUTTONS + false, // OP_TAKE_AUDIO_FOCUS + false, // OP_AUDIO_MASTER_VOLUME + false, // OP_AUDIO_VOICE_VOLUME + false, // OP_AUDIO_RING_VOLUME + false, // OP_AUDIO_MEDIA_VOLUME + false, // OP_AUDIO_ALARM_VOLUME + false, // OP_AUDIO_NOTIFICATION_VOLUME + false, // OP_AUDIO_BLUETOOTH_VOLUME + false, // OP_WAKE_LOCK + false, // OP_MONITOR_LOCATION + true, // OP_MONITOR_HIGH_POWER_LOCATION + false, // OP_GET_USAGE_STATS + false, // OP_MUTE_MICROPHONE + false, // OP_TOAST_WINDOW + false, // OP_PROJECT_MEDIA + false, // OP_ACTIVATE_VPN + true, // OP WALLPAPER + false, //ASSIST_STRUCTURE + false, //ASSIST_SCREENSHOT + false, //READ_PHONE_STATE + false, //ADD_VOICEMAIL + false, // USE_SIP + false, // PROCESS_OUTGOING_CALLS + false, // USE_FINGERPRINT + false, // BODY_SENSORS + false, // READ_CELL_BROADCASTS + false, // MOCK_LOCATION + true, // READ_EXTERNAL_STORAGE + true, // WRITE_EXTERNAL_STORAGE + false, // TURN_ON_SCREEN + false, // GET_ACCOUNTS + true, // OP_WIFI_CHANGE + true, // OP_BLUETOOTH_CHANGE + false, // OP_BOOT_COMPLETED + true, // OP_NFC_CHANGE + true, // OP_DATA_CONNECT_CHANGE + true, // OP_SU }; /** @@ -901,7 +1136,13 @@ public class AppOpsManager { false, false, false, - false + false, + false, // OP_WIFI_CHANGE + false, // OP_BLUETOOTH_CHANGE + false, // OP_BOOT_COMPLETED + false, // OP_NFC_CHANGE + false, // OP_DATA_CONNECT_CHANGE + false, // OP_SU }; /** @@ -914,6 +1155,8 @@ public class AppOpsManager { */ private static HashMap sPermToOp = new HashMap<>(); + private static HashMap sNameToOp = new HashMap(); + static { if (sOpToSwitch.length != _NUM_OP) { throw new IllegalStateException("sOpToSwitch length " + sOpToSwitch.length @@ -935,6 +1178,10 @@ public class AppOpsManager { throw new IllegalStateException("sOpDefaultMode length " + sOpDefaultMode.length + " should be " + _NUM_OP); } + if (sOpDefaultStrictMode.length != _NUM_OP) { + throw new IllegalStateException("sOpDefaultStrictMode length " + sOpDefaultStrictMode.length + + " should be " + _NUM_OP); + } if (sOpDisableReset.length != _NUM_OP) { throw new IllegalStateException("sOpDisableReset length " + sOpDisableReset.length + " should be " + _NUM_OP); @@ -947,6 +1194,10 @@ public class AppOpsManager { throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length " + sOpRestrictions.length + " should be " + _NUM_OP); } + if (sOpStrictMode.length != _NUM_OP) { + throw new IllegalStateException("sOpStrictMode length " + + sOpStrictMode.length + " should be " + _NUM_OP); + } for (int i=0; i<_NUM_OP; i++) { if (sOpToString[i] != null) { sOpStrToOp.put(sOpToString[i], i); @@ -957,6 +1208,9 @@ public class AppOpsManager { sPermToOp.put(sOpPerms[i], i); } } + for (int i=0; i<_NUM_OP; i++) { + sNameToOp.put(sOpNames[i], i); + } } /** @@ -988,6 +1242,15 @@ public static int strDebugOpToOp(String op) { throw new IllegalArgumentException("Unknown operation string: " + op); } + /** + * Map a non-localized name for the operation back to the Op number + * @hide + */ + public static int nameToOp(String name) { + Integer val = sNameToOp.get(name); + return val != null ? val : OP_NONE; + } + /** * Retrieve the permission associated with an operation, or null if there is not one. * @hide @@ -1026,7 +1289,9 @@ public static boolean opAllowSystemBypassRestriction(int op) { * Retrieve the default mode for the operation. * @hide */ - public static int opToDefaultMode(int op) { + public static int opToDefaultMode(int op, boolean isStrict) { + if (isStrict) + return sOpDefaultStrictMode[op]; return sOpDefaultMode[op]; } @@ -1113,9 +1378,11 @@ public static class OpEntry implements Parcelable { private final int mDuration; private final int mProxyUid; private final String mProxyPackageName; + private final int mAllowedCount; + private final int mIgnoredCount; public OpEntry(int op, int mode, long time, long rejectTime, int duration, - int proxyUid, String proxyPackage) { + int proxyUid, String proxyPackage, int allowedCount, int ignoredCount) { mOp = op; mMode = mode; mTime = time; @@ -1123,6 +1390,8 @@ public OpEntry(int op, int mode, long time, long rejectTime, int duration, mDuration = duration; mProxyUid = proxyUid; mProxyPackageName = proxyPackage; + mAllowedCount = allowedCount; + mIgnoredCount = ignoredCount; } public int getOp() { @@ -1157,6 +1426,14 @@ public String getProxyPackageName() { return mProxyPackageName; } + public int getAllowedCount() { + return mAllowedCount; + } + + public int getIgnoredCount() { + return mIgnoredCount; + } + @Override public int describeContents() { return 0; @@ -1171,6 +1448,8 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mDuration); dest.writeInt(mProxyUid); dest.writeString(mProxyPackageName); + dest.writeInt(mAllowedCount); + dest.writeInt(mIgnoredCount); } OpEntry(Parcel source) { @@ -1181,6 +1460,8 @@ public void writeToParcel(Parcel dest, int flags) { mDuration = source.readInt(); mProxyUid = source.readInt(); mProxyPackageName = source.readString(); + mAllowedCount = source.readInt(); + mIgnoredCount = source.readInt(); } public static final Creator CREATOR = new Creator() { @@ -1753,4 +2034,75 @@ public void finishOp(int op, int uid, String packageName) { public void finishOp(int op) { finishOp(op, Process.myUid(), mContext.getOpPackageName()); } + + /** @hide */ + public static boolean isStrictEnable() { + return SystemProperties.getBoolean("persist.sys.strict_op_enable", false); + } + + /** + * Check if op in strict mode + * @hide + */ + public static boolean isStrictOp(int code) { + return sOpStrictMode[code]; + } + + + /** @hide */ + public static int stringToMode(String permission) { + if ("allowed".equalsIgnoreCase(permission)) { + return AppOpsManager.MODE_ALLOWED; + } else if ("ignored".equalsIgnoreCase(permission)) { + return AppOpsManager.MODE_IGNORED; + } else if ("ask".equalsIgnoreCase(permission)) { + return AppOpsManager.MODE_ASK; + } + return AppOpsManager.MODE_ERRORED; + } + + /** @hide */ + public static int stringOpToOp (String op) { + Integer val = sOpStrToOp.get(op); + if (val == null) { + val = OP_NONE; + } + return val; + } + + /** @hide */ + public boolean isControlAllowed(int op, String packageName) { + boolean isShow = true; + try { + isShow = mService.isControlAllowed(op, packageName); + } catch (RemoteException e) { + } + return isShow; + } + + /** @hide */ + public boolean getPrivacyGuardSettingForPackage(int uid, String packageName) { + try { + return mService.getPrivacyGuardSettingForPackage(uid, packageName); + } catch (RemoteException e) { + } + return false; + } + + /** @hide */ + public void setPrivacyGuardSettingForPackage(int uid, String packageName, + boolean state) { + try { + mService.setPrivacyGuardSettingForPackage(uid, packageName, state); + } catch (RemoteException e) { + } + } + + /** @hide */ + public void resetCounters() { + try { + mService.resetCounters(); + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 7cae745fcf29a..c829daa9eed1e 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -23,6 +23,7 @@ import android.annotation.XmlRes; import android.content.ComponentName; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; @@ -1028,12 +1029,13 @@ public Resources getResourcesForApplication(@NonNull ApplicationInfo app) if (app.packageName.equals("system")) { return mContext.mMainThread.getSystemContext().getResources(); } + final boolean sameUid = (app.uid == Process.myUid()); final Resources r = mContext.mMainThread.getTopLevelResources( sameUid ? app.sourceDir : app.publicSourceDir, sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY, - null, mContext.mPackageInfo); + null, mContext.mPackageInfo, mContext, app.packageName); if (r != null) { return r; } @@ -1069,6 +1071,49 @@ public Resources getResourcesForApplicationAsUser(String appPackageName, int use throw new NameNotFoundException("Package " + appPackageName + " doesn't exist"); } + /** @hide */ + @Override public Resources getThemedResourcesForApplication( + ApplicationInfo app, String themePkgName) throws NameNotFoundException { + if (app.packageName.equals("system")) { + return mContext.mMainThread.getSystemContext().getResources(); + } + + Resources r = mContext.mMainThread.getTopLevelThemedResources( + app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir, + Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo, app.packageName, + themePkgName); + if (r != null) { + return r; + } + throw new NameNotFoundException("Unable to open " + app.publicSourceDir); + } + + /** @hide */ + @Override public Resources getThemedResourcesForApplication( + String appPackageName, String themePkgName) throws NameNotFoundException { + return getThemedResourcesForApplication( + getApplicationInfo(appPackageName, 0), themePkgName); + } + + /** @hide */ + @Override + public Resources getThemedResourcesForApplicationAsUser(String appPackageName, + String themePackageName, int userId) throws NameNotFoundException { + if (userId < 0) { + throw new IllegalArgumentException( + "Call does not support special user #" + userId); + } + try { + ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, 0, userId); + if (ai != null) { + return getThemedResourcesForApplication(ai, themePackageName); + } + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + throw new NameNotFoundException("Package " + appPackageName + " doesn't exist"); + } + int mCachedSafeMode = -1; @Override public boolean isSafeMode() { try { @@ -1992,6 +2037,28 @@ public boolean isUpgrade() { } } + @Override + public void setComponentProtectedSetting(ComponentName componentName, boolean newState) { + try { + mPM.setComponentProtectedSetting(componentName, newState, mContext.getUserId()); + } catch (RemoteException re) { + Log.e(TAG, "Failed to set component protected setting", re); + } + } + + /** @hide */ + @Override + public boolean isComponentProtected(String callingPackage, int callingUid, + ComponentName componentName) { + try { + return mPM.isComponentProtected(callingPackage, callingUid, componentName, + mContext.getUserId()); + } catch (RemoteException re) { + Log.e(TAG, "Failed to get component protected setting", re); + return false; + } + } + @Override public PackageInstaller getPackageInstaller() { synchronized (mLock) { @@ -2240,4 +2307,30 @@ public boolean handleMessage(Message msg) { return false; } } + + /** + * @hide + */ + @Override + public void updateIconMaps(String pkgName) { + try { + mPM.updateIconMapping(pkgName); + } catch (RemoteException re) { + Log.e(TAG, "Failed to update icon maps", re); + } + } + + /** + * @hide + */ + @Override + public int processThemeResources(String themePkgName) { + try { + return mPM.processThemeResources(themePkgName); + } catch (RemoteException e) { + Log.e(TAG, "Unable to process theme resources for " + themePkgName, e); + } + + return 0; + } } diff --git a/core/java/android/app/ComposedIconInfo.aidl b/core/java/android/app/ComposedIconInfo.aidl new file mode 100644 index 0000000000000..8a1bab52d8c3a --- /dev/null +++ b/core/java/android/app/ComposedIconInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +/** @hide */ +parcelable ComposedIconInfo; diff --git a/core/java/android/app/ComposedIconInfo.java b/core/java/android/app/ComposedIconInfo.java new file mode 100644 index 0000000000000..71321c1faf4ac --- /dev/null +++ b/core/java/android/app/ComposedIconInfo.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class ComposedIconInfo implements Parcelable { + public int iconUpon, iconMask; + public int[] iconBacks; + public float iconScale; + public float iconRotation; + // value used to provide some randomization to the angle of rotation + public float iconRotationVariance; + public float iconTranslationX; + public float iconTranslationY; + public int iconDensity; + public int iconSize; + public float[] colorFilter; + + // Palettized background items + public int iconPaletteBack; + public SwatchType swatchType; + public int[] defaultSwatchColors; + + public ComposedIconInfo() { + super(); + iconPaletteBack = 0; + swatchType = SwatchType.None; + iconRotation = 0; + iconTranslationX = 0; + iconTranslationY = 0; + iconScale = 1f; + } + + private ComposedIconInfo(Parcel source) { + iconScale = source.readFloat(); + iconRotation = source.readFloat(); + iconRotationVariance = source.readFloat(); + iconTranslationX = source.readFloat(); + iconTranslationY = source.readFloat(); + iconDensity = source.readInt(); + iconSize = source.readInt(); + int backCount = source.readInt(); + if (backCount > 0) { + iconBacks = new int[backCount]; + for (int i = 0; i < backCount; i++) { + iconBacks[i] = source.readInt(); + } + } + iconMask = source.readInt(); + iconUpon = source.readInt(); + int colorFilterSize = source.readInt(); + if (colorFilterSize > 0) { + colorFilter = new float[colorFilterSize]; + for (int i = 0; i < colorFilterSize; i++) { + colorFilter[i] = source.readFloat(); + } + } + iconPaletteBack = source.readInt(); + swatchType = SwatchType.values()[source.readInt()]; + int numDefaultColors = source.readInt(); + defaultSwatchColors = new int[numDefaultColors]; + for (int i = 0; i < numDefaultColors; i++) { + defaultSwatchColors[i] = source.readInt(); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(iconScale); + dest.writeFloat(iconRotation); + dest.writeFloat(iconRotationVariance); + dest.writeFloat(iconTranslationX); + dest.writeFloat(iconTranslationY); + dest.writeInt(iconDensity); + dest.writeInt(iconSize); + dest.writeInt(iconBacks != null ? iconBacks.length : 0); + if (iconBacks != null) { + for (int resId : iconBacks) { + dest.writeInt(resId); + } + } + dest.writeInt(iconMask); + dest.writeInt(iconUpon); + if (colorFilter != null) { + dest.writeInt(colorFilter.length); + for (float val : colorFilter) { + dest.writeFloat(val); + } + } else { + dest.writeInt(0); + } + dest.writeInt(iconPaletteBack); + dest.writeInt(swatchType.ordinal()); + if (defaultSwatchColors != null) { + dest.writeInt(defaultSwatchColors.length); + for (int color : defaultSwatchColors) { + dest.writeInt(color); + } + } else { + dest.writeInt(0); + } + } + + public static final Creator CREATOR + = new Creator() { + @Override + public ComposedIconInfo createFromParcel(Parcel source) { + return new ComposedIconInfo(source); + } + + @Override + public ComposedIconInfo[] newArray(int size) { + return new ComposedIconInfo[0]; + } + }; + + public enum SwatchType { + None, + Vibrant, + VibrantLight, + VibrantDark, + Muted, + MutedLight, + MutedDark + } +} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 235f29439893e..6896c21bcabf2 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -74,6 +74,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; class ReceiverRestrictedContext extends ContextWrapper { ReceiverRestrictedContext(Context base) { @@ -268,6 +269,15 @@ private void initializeTheme() { mTheme.applyStyle(mThemeResource, true); } + @Override + public void recreateTheme() { + if (mTheme != null) { + Resources.Theme newTheme = mResources.newTheme(); + newTheme.applyStyle(mThemeResource, true); + mTheme.setTo(newTheme); + } + } + @Override public ClassLoader getClassLoader() { return mPackageInfo != null ? @@ -1657,13 +1667,19 @@ private void warnIfCallingFromSystemProcess() { @Override public Context createApplicationContext(ApplicationInfo application, int flags) throws NameNotFoundException { + return createApplicationContext(application, null, flags); + } + + @Override + public Context createApplicationContext(ApplicationInfo application, String themePackageName, + int flags) throws NameNotFoundException { LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE); if (pi != null) { final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, new UserHandle(UserHandle.getUserId(application.uid)), restricted, - mDisplay, null, Display.INVALID_DISPLAY); + mDisplay, null, Display.INVALID_DISPLAY, themePackageName); if (c.mResources != null) { return c; } @@ -1676,24 +1692,30 @@ public Context createApplicationContext(ApplicationInfo application, int flags) @Override public Context createPackageContext(String packageName, int flags) throws NameNotFoundException { - return createPackageContextAsUser(packageName, flags, + return createPackageContextAsUser(packageName, null, flags, mUser != null ? mUser : Process.myUserHandle()); } @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws NameNotFoundException { + return createPackageContextAsUser(packageName, null, flags, user); + } + + @Override + public Context createPackageContextAsUser(String packageName, String themePackageName, + int flags, UserHandle user) throws NameNotFoundException { final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; if (packageName.equals("system") || packageName.equals("android")) { return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, - user, restricted, mDisplay, null, Display.INVALID_DISPLAY); + user, restricted, mDisplay, null, Display.INVALID_DISPLAY, themePackageName); } LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, - user, restricted, mDisplay, null, Display.INVALID_DISPLAY); + user, restricted, mDisplay, null, Display.INVALID_DISPLAY, themePackageName); if (c.mResources != null) { return c; } @@ -1774,7 +1796,7 @@ public int getUserId() { static ContextImpl createSystemContext(ActivityThread mainThread) { LoadedApk packageInfo = new LoadedApk(mainThread); ContextImpl context = new ContextImpl(null, mainThread, - packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY); + packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY, null); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetricsLocked()); return context; @@ -1783,19 +1805,27 @@ static ContextImpl createSystemContext(ActivityThread mainThread) { static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); return new ContextImpl(null, mainThread, - packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY); + packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY, null); } static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); return new ContextImpl(null, mainThread, packageInfo, null, null, false, - null, overrideConfiguration, displayId); + null, overrideConfiguration, displayId, null); } private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration, int createDisplayWithId) { + this(container, mainThread, packageInfo, activityToken, user, restricted, display, + overrideConfiguration, createDisplayWithId, null); + } + + private ContextImpl(ContextImpl container, ActivityThread mainThread, + LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, + Display display, Configuration overrideConfiguration, int createDisplayWithId, + String themePackageName) { mOuterContext = this; mMainThread = mainThread; @@ -1829,16 +1859,27 @@ private ContextImpl(ContextImpl container, ActivityThread mainThread, mDisplay = (createDisplayWithId == Display.INVALID_DISPLAY) ? display : ResourcesManager.getInstance().getAdjustedDisplay(displayId, mDisplayAdjustments); + // We need to create the content resolver before all the context resources creation because + // the content resolver is reference by the outer context while the theme information + // is created. + mContentResolver = new ApplicationContentResolver(this, mainThread, user); + Resources resources = packageInfo.getResources(mainThread); if (resources != null) { if (displayId != Display.DEFAULT_DISPLAY + || themePackageName != null || overrideConfiguration != null || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { - resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(), - packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(), + resources = themePackageName == null ? mResourcesManager.getTopLevelResources( + packageInfo.getResDir(), packageInfo.getSplitResDirs(), + packageInfo.getOverlayDirs(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, - overrideConfiguration, compatInfo); + packageInfo.getAppDir(), overrideConfiguration, compatInfo, mOuterContext, + packageInfo.getApplicationInfo().isThemeable) : + mResourcesManager.getTopLevelThemedResources(packageInfo.getResDir(), displayId, + packageInfo.getPackageName(), themePackageName, compatInfo, + packageInfo.getApplicationInfo().isThemeable); } } mResources = resources; @@ -1859,8 +1900,6 @@ private ContextImpl(ContextImpl container, ActivityThread mainThread, mOpPackageName = mBasePackageName; } } - - mContentResolver = new ApplicationContentResolver(this, mainThread, user); } void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { @@ -1950,9 +1989,12 @@ private File makeFilename(File base, String name) { * unable to create, they are filtered by replacing with {@code null}. */ private File[] ensureDirsExistOrFilter(File[] dirs) { - File[] result = new File[dirs.length]; + ArrayList result = new ArrayList(dirs.length); for (int i = 0; i < dirs.length; i++) { File dir = dirs[i]; + if (Environment.MEDIA_REMOVED.equals(Environment.getStorageState(dir))) { + continue; + } if (!dir.exists()) { if (!dir.mkdirs()) { // recheck existence in case of cross-process race @@ -1974,9 +2016,14 @@ private File[] ensureDirsExistOrFilter(File[] dirs) { } } } - result[i] = dir; + result.add(dir); + } + + // Make sure there is at least one element, let the callers handle that + if (result.size() == 0) { + result.add(null); } - return result; + return result.toArray(new File[result.size()]); } // ---------------------------------------------------------------------- diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index fb0e79b849b28..b382b7615b0f6 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -266,6 +266,13 @@ public class DownloadManager { */ public final static int PAUSED_UNKNOWN = 4; + /** + * Value of {@link #COLUMN_REASON} when the download is paused by manual. + * + * @hide + */ + public final static int PAUSED_BY_MANUAL = 5; + /** * Broadcast intent action sent by the download manager when a download completes. */ @@ -865,6 +872,7 @@ Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) { parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY)); parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK)); parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI)); + parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_BY_MANUAL)); } if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) { parts.add(statusClause("=", Downloads.Impl.STATUS_SUCCESS)); @@ -1030,7 +1038,7 @@ public Uri getUriForDownloadedFile(long id) { if (cursor.moveToFirst()) { int status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS)); if (DownloadManager.STATUS_SUCCESSFUL == status) { - return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, id); + return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id); } } } finally { @@ -1099,6 +1107,34 @@ public void restartDownload(long... ids) { mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); } + /** + * Pause the given running download by manual. + * + * @param id the ID of the download to be paused + * @return the number of downloads actually updated + * @hide + */ + public int pauseDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PAUSED_BY_MANUAL); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + + /** + * Resume the given paused download by manual. + * + * @param id the ID of the download to be resumed + * @return the number of downloads actually updated + * @hide + */ + public int resumeDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_RUNNING); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + /** * Returns maximum size, in bytes, of downloads that may go over a mobile connection; or null if * there's no limit @@ -1227,7 +1263,7 @@ private static void validateArgumentIsNonEmpty(String paramName, String val) { * @hide */ public Uri getDownloadUri(long id) { - return ContentUris.withAppendedId(mBaseUri, id); + return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id); } /** @@ -1308,7 +1344,7 @@ private String getLocalUri() { // return content URI for cache download long downloadId = getLong(getColumnIndex(Downloads.Impl._ID)); - return ContentUris.withAppendedId(mBaseUri, downloadId).toString(); + return ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, downloadId).toString(); } private long getReason(int status) { @@ -1335,6 +1371,9 @@ private long getPausedReason(int status) { case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: return PAUSED_QUEUED_FOR_WIFI; + case Downloads.Impl.STATUS_PAUSED_BY_MANUAL: + return PAUSED_BY_MANUAL; + default: return PAUSED_UNKNOWN; } @@ -1390,6 +1429,7 @@ private int translateStatus(int status) { case Downloads.Impl.STATUS_WAITING_TO_RETRY: case Downloads.Impl.STATUS_WAITING_FOR_NETWORK: case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: + case Downloads.Impl.STATUS_PAUSED_BY_MANUAL: return STATUS_PAUSED; case Downloads.Impl.STATUS_SUCCESS: diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index ef121ce2d8063..75951cfeb0ff8 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -122,12 +122,14 @@ public void activityStopped(IBinder token, Bundle state, public void activitySlept(IBinder token) throws RemoteException; public void activityDestroyed(IBinder token) throws RemoteException; public String getCallingPackage(IBinder token) throws RemoteException; + public String getCallingPackageForBroadcast(boolean foreground) throws RemoteException; public ComponentName getCallingActivity(IBinder token) throws RemoteException; public List getAppTasks(String callingPackage) throws RemoteException; public int addAppTask(IBinder activityToken, Intent intent, ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException; public Point getAppTaskThumbnailSize() throws RemoteException; public List getTasks(int maxNum, int flags) throws RemoteException; + public boolean isPackageInForeground(String packageName) throws RemoteException; public List getRecentTasks(int maxNum, int flags, int userId) throws RemoteException; public ActivityManager.TaskThumbnail getTaskThumbnail(int taskId) throws RemoteException; @@ -182,7 +184,7 @@ public void serviceDoneExecuting(IBinder token, int type, int startId, public IBinder peekService(Intent service, String resolvedType, String callingPackage) throws RemoteException; - public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode) + public boolean bindBackupAgent(String packageName, int backupRestoreMode, int userId) throws RemoteException; public void clearPendingBackup() throws RemoteException; public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException; @@ -406,12 +408,14 @@ public void setPackageAskScreenCompat(String packageName, boolean ask) public long[] getProcessPss(int[] pids) throws RemoteException; - public void showBootMessage(CharSequence msg, boolean always) throws RemoteException; + public void updateBootProgress(int stage, ApplicationInfo optimizedApp, + int currentAppPos, int totalAppCount, boolean always) throws RemoteException; public void keyguardWaitingForActivityDrawn() throws RemoteException; public void keyguardGoingAway(boolean disableWindowAnimations, - boolean keyguardGoingToNotificationShade) throws RemoteException; + boolean keyguardGoingToNotificationShade, + boolean keyguardShowingMedia) throws RemoteException; public boolean shouldUpRecreateTask(IBinder token, String destAffinity) throws RemoteException; @@ -619,6 +623,11 @@ private WaitResult(Parcel source) { } } + public static final int BOOT_STAGE_STARTING_APPS = 1; + public static final int BOOT_STAGE_FSTRIM = 2; + public static final int BOOT_STAGE_PREPARING_APPS = 3; + public static final int BOOT_STAGE_COMPLETE = 4; + String descriptor = "android.app.IActivityManager"; // Please keep these transaction codes the same -- they are also @@ -754,7 +763,7 @@ private WaitResult(Parcel source) { int IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+134; int UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+135; int GET_PROCESS_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+136; - int SHOW_BOOT_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137; + int UPDATE_BOOT_PROGRESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137; int KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+139; int GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+140; int REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+141; @@ -839,6 +848,10 @@ private WaitResult(Parcel source) { int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240; int CHECK_PERMISSION_WITH_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+241; int REGISTER_TASK_STACK_LISTENER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+242; + // 243: Available + + // start of CM transactions + int GET_CALLING_PACKAGE_FOR_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+244; // Start of M transactions int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+280; @@ -864,4 +877,5 @@ private WaitResult(Parcel source) { = IBinder.FIRST_CALL_TRANSACTION+299; int SHOW_ASSIST_FROM_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+300; int IS_ROOT_VOICE_INTERACTION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+301; + int IS_PACKAGE_IN_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+302; } diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl index 327c00b8f659e..4fdbfaa2b8074 100644 --- a/core/java/android/app/IAlarmManager.aidl +++ b/core/java/android/app/IAlarmManager.aidl @@ -35,6 +35,8 @@ interface IAlarmManager { void remove(in PendingIntent operation); long getNextWakeFromIdleTime(); AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); + // update the uids being synchronized by network socket request manager + void updateBlockedUids(int uid, boolean isBlocked); } diff --git a/core/java/android/app/IBatteryService.aidl b/core/java/android/app/IBatteryService.aidl new file mode 100644 index 0000000000000..196159b170685 --- /dev/null +++ b/core/java/android/app/IBatteryService.aidl @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2016, The CyanogenMod 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; + +/** + * System private API for talking with the battery service. + * + * {@hide} + */ +interface IBatteryService { + boolean isDockBatterySupported(); +} diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index f78fb47e5f5e9..06c064f0e9d67 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -55,6 +55,12 @@ interface INotificationManager void setPackageVisibilityOverride(String pkg, int uid, int visibility); int getPackageVisibilityOverride(String pkg, int uid); + void setShowNotificationForPackageOnKeyguard(String pkg, int uid, int status); + int getShowNotificationForPackageOnKeyguard(String pkg, int uid); + + void setPackageNotificationSoundTimeout(String pkg, int uid, long timeout); + long getPackageNotificationSoundTimeout(String pkg, int uid); + // TODO: Remove this when callers have been migrated to the equivalent // INotificationListener method. StatusBarNotification[] getActiveNotifications(String callingPkg); @@ -77,7 +83,7 @@ interface INotificationManager void setInterruptionFilter(String pkg, int interruptionFilter); ComponentName getEffectsSuppressor(); - boolean matchesCallFilter(in Bundle extras); + boolean[] matchesCallFilter(in Bundle extras); boolean isSystemConditionProviderEnabled(String path); int getZenMode(); @@ -97,4 +103,6 @@ interface INotificationManager void applyRestore(in byte[] payload, int user); ParceledListSlice getAppActiveNotifications(String callingPkg, int userId); + + boolean deviceLightsCan(int lightCapability); } diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index ccba250001b69..c717459475a2a 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -30,6 +30,12 @@ interface IWallpaperManager { * Set the wallpaper. */ ParcelFileDescriptor setWallpaper(String name, in String callingPackage); + + /** + * Set the keyguard wallpaper. + * @hide + */ + ParcelFileDescriptor setKeyguardWallpaper(String name, in String callingPackage); /** * Set the live wallpaper. @@ -46,6 +52,13 @@ interface IWallpaperManager { */ ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, out Bundle outParams); + + /** + * Get the keyguard wallpaper. + * @hide + */ + ParcelFileDescriptor getKeyguardWallpaper(IWallpaperManagerCallback cb, + out Bundle outParams); /** * Get information about a live wallpaper. @@ -57,6 +70,12 @@ interface IWallpaperManager { */ void clearWallpaper(in String callingPackage); + /* + * Clear the keyguard wallpaper. + * @hide + */ + void clearKeyguardWallpaper(); + /** * Return whether there is a wallpaper set with the given name. */ diff --git a/core/java/android/app/IWallpaperManagerCallback.aidl b/core/java/android/app/IWallpaperManagerCallback.aidl index 991b2bc924b5f..b217318291da5 100644 --- a/core/java/android/app/IWallpaperManagerCallback.aidl +++ b/core/java/android/app/IWallpaperManagerCallback.aidl @@ -28,4 +28,9 @@ oneway interface IWallpaperManagerCallback { * Called when the wallpaper has changed */ void onWallpaperChanged(); + + /** + * Called when the keygaurd wallpaper has changed + */ + void onKeyguardWallpaperChanged(); } diff --git a/core/java/android/app/IconPackHelper.java b/core/java/android/app/IconPackHelper.java new file mode 100644 index 0000000000000..9c71ddd5cbf5e --- /dev/null +++ b/core/java/android/app/IconPackHelper.java @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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 java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import android.content.pm.PackageInfo; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.PaintDrawable; +import android.graphics.drawable.VectorDrawable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.util.TypedValue; + +import com.android.internal.util.cm.palette.Palette; + +import org.cyanogenmod.internal.themes.IIconCacheManager; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ThemeUtils; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.DisplayMetrics; + +/** @hide */ +public class IconPackHelper { + private static final String TAG = IconPackHelper.class.getSimpleName(); + + private static final boolean DEBUG = false; + + private static final String ICON_MASK_TAG = "iconmask"; + private static final String ICON_BACK_TAG = "iconback"; + private static final String ICON_UPON_TAG = "iconupon"; + private static final String ICON_SCALE_TAG = "scale"; + private static final String ICON_ROTATE_TAG = "rotate"; + private static final String ICON_TRANSLATE_TAG = "translate"; + private static final String ICON_BACK_FORMAT = "iconback%d"; + + // Palettized icon background constants + private static final String ICON_PALETTIZED_BACK_TAG = "paletteback"; + private static final String IMG_ATTR = "img"; + private static final String SWATCH_TYPE_ATTR = "swatchType"; + private static final String DEFAULT_SWATCH_COLOR_ATTR = "defaultSwatchColor"; + private static final String VIBRANT_VALUE = "vibrant"; + private static final String VIBRANT_LIGHT_VALUE = "vibrantLight"; + private static final String VIBRANT_DARK_VALUE = "vibrantDark"; + private static final String MUTED_VALUE = "muted"; + private static final String MUTED_LIGHT_VALUE = "mutedLight"; + private static final String MUTED_DARK_VALUE = "mutedDark"; + private static final int NUM_PALETTE_COLORS = 32; + + // Rotation and translation constants + private static final String ANGLE_ATTR = "angle"; + private static final String ANGLE_VARIANCE = "plusMinus"; + private static final String TRANSLATE_X_ATTR = "xOffset"; + private static final String TRANSLATE_Y_ATTR = "yOffset"; + + private static final ComponentName ICON_BACK_COMPONENT; + private static final ComponentName ICON_MASK_COMPONENT; + private static final ComponentName ICON_UPON_COMPONENT; + private static final ComponentName ICON_SCALE_COMPONENT; + + private static final float DEFAULT_SCALE = 1.0f; + private static final int COMPOSED_ICON_COOKIE = 128; + + private static final String ICON_CACHE_SERVICE = "cmiconcache"; + + public static final String SYSTEM_THEME_PATH = "/data/system/theme"; + public static final String SYSTEM_THEME_ICON_CACHE_DIR = SYSTEM_THEME_PATH + + File.separator + "icons"; + + private final Context mContext; + private Map mIconPackResourceMap; + private String mLoadedIconPackName; + private Resources mLoadedIconPackResource; + private ComposedIconInfo mComposedIconInfo; + private int mIconBackCount = 0; + private ColorFilterUtils.Builder mFilterBuilder; + + static { + ICON_BACK_COMPONENT = new ComponentName(ICON_BACK_TAG, ""); + ICON_MASK_COMPONENT = new ComponentName(ICON_MASK_TAG, ""); + ICON_UPON_COMPONENT = new ComponentName(ICON_UPON_TAG, ""); + ICON_SCALE_COMPONENT = new ComponentName(ICON_SCALE_TAG, ""); + } + + public IconPackHelper(Context context) { + mContext = context; + mIconPackResourceMap = new HashMap(); + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + mComposedIconInfo = new ComposedIconInfo(); + mComposedIconInfo.iconSize = am.getLauncherLargeIconSize(); + mComposedIconInfo.iconDensity = am.getLauncherLargeIconDensity(); + mFilterBuilder = new ColorFilterUtils.Builder(); + } + + private void loadResourcesFromXmlParser(XmlPullParser parser, + Map iconPackResources) + throws XmlPullParserException, IOException { + mIconBackCount = 0; + int eventType = parser.getEventType(); + do { + + if (eventType != XmlPullParser.START_TAG) { + continue; + } + + if (parseComposedIconComponent(parser, iconPackResources)) { + continue; + } + + if (ColorFilterUtils.parseIconFilter(parser, mFilterBuilder)) { + continue; + } + + if (parser.getName().equalsIgnoreCase(ICON_SCALE_TAG)) { + String factor = parser.getAttributeValue(null, "factor"); + if (factor == null) { + if (parser.getAttributeCount() == 1) { + factor = parser.getAttributeValue(0); + } + } + iconPackResources.put(ICON_SCALE_COMPONENT, factor); + continue; + } + + if (parseRotationComponent(parser, mComposedIconInfo)) { + continue; + } + + if (parseTranslationComponent(parser, mComposedIconInfo)) { + continue; + } + + if (!parser.getName().equalsIgnoreCase("item")) { + continue; + } + + String component = parser.getAttributeValue(null, "component"); + String drawable = parser.getAttributeValue(null, "drawable"); + + // Validate component/drawable exist + if (TextUtils.isEmpty(component) || TextUtils.isEmpty(drawable)) { + continue; + } + + // Validate format/length of component + if (!component.startsWith("ComponentInfo{") || !component.endsWith("}") + || component.length() < 16 || drawable.length() == 0) { + continue; + } + + // Sanitize stored value + component = component.substring(14, component.length() - 1).toLowerCase(); + + ComponentName name = null; + if (!component.contains("/")) { + // Package icon reference + name = new ComponentName(component.toLowerCase(), ""); + } else { + name = ComponentName.unflattenFromString(component); + } + + if (name != null) { + iconPackResources.put(name, drawable); + } + } while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT); + } + + private boolean isComposedIconComponent(String tag) { + return ICON_MASK_TAG.equalsIgnoreCase(tag) || + ICON_BACK_TAG.equalsIgnoreCase(tag) || + ICON_UPON_TAG.equalsIgnoreCase(tag) || + ICON_PALETTIZED_BACK_TAG.equalsIgnoreCase(tag); + } + + private boolean parseComposedIconComponent(XmlPullParser parser, + Map iconPackResources) { + String icon; + String tag = parser.getName(); + if (!isComposedIconComponent(tag)) { + return false; + } + + if (parser.getAttributeCount() >= 1) { + if (ICON_BACK_TAG.equalsIgnoreCase(tag)) { + mIconBackCount = parser.getAttributeCount(); + for (int i = 0; i < mIconBackCount; i++) { + tag = String.format(ICON_BACK_FORMAT, i); + icon = parser.getAttributeValue(i); + iconPackResources.put(new ComponentName(tag, ""), icon); + } + } else if (ICON_PALETTIZED_BACK_TAG.equalsIgnoreCase(tag)) { + parsePalettizedBackground(parser, mComposedIconInfo); + } else { + icon = parser.getAttributeValue(0); + iconPackResources.put(new ComponentName(tag, ""), + icon); + } + return true; + } + + return false; + } + + private void parsePalettizedBackground(XmlPullParser parser, ComposedIconInfo iconInfo) { + int attrCount = parser.getAttributeCount(); + ArrayList convertedColors = new ArrayList(); + for (int i = 0; i < attrCount; i++) { + String name = parser.getAttributeName(i); + String value = parser.getAttributeValue(i); + if (TextUtils.isEmpty(name)) { + Log.w(TAG, "Attribute name cannot be empty or null"); + continue; + } + if (TextUtils.isEmpty(value)) { + Log.w(TAG, "Attribute value cannot be empty or null"); + continue; + } + if (IMG_ATTR.equalsIgnoreCase(name)) { + iconInfo.iconPaletteBack = getResourceIdForDrawable(value); + if (DEBUG) { + Log.d(TAG, String.format("img=%s, resId=%d", value, + iconInfo.iconPaletteBack)); + } + } else if (SWATCH_TYPE_ATTR.equalsIgnoreCase(name)) { + ComposedIconInfo.SwatchType type = ComposedIconInfo.SwatchType.None; + if (VIBRANT_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.Vibrant; + } else if (VIBRANT_LIGHT_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.VibrantLight; + } else if (VIBRANT_DARK_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.VibrantDark; + } else if (MUTED_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.Muted; + } else if (MUTED_LIGHT_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.MutedLight; + } else if (MUTED_DARK_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.MutedDark; + } + if (type != ComposedIconInfo.SwatchType.None) { + iconInfo.swatchType = type; + if (DEBUG) Log.d(TAG, "PaletteType=" + type); + } + } else if (name.startsWith(DEFAULT_SWATCH_COLOR_ATTR)) { + try { + // ensure alpha is always 0xff + convertedColors.add(Color.parseColor(value) | 0xff000000); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Invalid color format", e); + } + } + if (convertedColors.size() > 0) { + iconInfo.defaultSwatchColors = new int[convertedColors.size()]; + for (int j = 0; j < convertedColors.size(); j++) { + iconInfo.defaultSwatchColors[j] = convertedColors.get(j); + } + } + } + } + + private boolean parseRotationComponent(XmlPullParser parser, ComposedIconInfo iconInfo) { + if (!parser.getName().equalsIgnoreCase(ICON_ROTATE_TAG)) return false; + String angle = parser.getAttributeValue(null, ANGLE_ATTR); + String variance = parser.getAttributeValue(null, ANGLE_VARIANCE); + if (angle != null) { + try { + iconInfo.iconRotation = Float.valueOf(angle); + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing " + ANGLE_ATTR, e); + } + } + if (variance != null) { + try { + iconInfo.iconRotationVariance = Float.valueOf(variance); + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing " + ANGLE_VARIANCE, e); + } + } + return true; + } + + private boolean parseTranslationComponent(XmlPullParser parser, ComposedIconInfo iconInfo) { + if (!parser.getName().equalsIgnoreCase(ICON_TRANSLATE_TAG)) return false; + + final float density = mContext.getResources().getDisplayMetrics().density; + String translateX = parser.getAttributeValue(null, TRANSLATE_X_ATTR); + String translateY = parser.getAttributeValue(null, TRANSLATE_Y_ATTR); + if (translateX != null) { + try { + iconInfo.iconTranslationX = Float.valueOf(translateX) * density; + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing " + TRANSLATE_X_ATTR, e); + } + } + if (translateY != null) { + try { + iconInfo.iconTranslationY = Float.valueOf(translateY) * density; + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing " + TRANSLATE_Y_ATTR, e); + } + } + return true; + } + + public void loadIconPack(String packageName) throws NameNotFoundException { + if (packageName == null) { + mLoadedIconPackResource = null; + mLoadedIconPackName = null; + mComposedIconInfo.iconBacks = null; + mComposedIconInfo.iconMask = mComposedIconInfo.iconUpon = 0; + mComposedIconInfo.iconScale = 0; + mComposedIconInfo.iconRotation = 0; + mComposedIconInfo.iconTranslationX = 0; + mComposedIconInfo.iconTranslationY = 0; + mComposedIconInfo.colorFilter = null; + mComposedIconInfo.iconPaletteBack = 0; + mComposedIconInfo.swatchType = ComposedIconInfo.SwatchType.None; + } else { + mIconBackCount = 0; + Resources res = createIconResource(mContext, packageName); + mLoadedIconPackResource = res; + mLoadedIconPackName = packageName; + mIconPackResourceMap = getIconResMapFromXml(res, packageName); + loadComposedIconComponents(); + ColorMatrix cm = mFilterBuilder.build(); + if (cm != null) { + mComposedIconInfo.colorFilter = cm.getArray().clone(); + } + } + } + + public ComposedIconInfo getComposedIconInfo() { + return mComposedIconInfo; + } + + private void loadComposedIconComponents() { + mComposedIconInfo.iconMask = getResourceIdForName(ICON_MASK_COMPONENT); + mComposedIconInfo.iconUpon = getResourceIdForName(ICON_UPON_COMPONENT); + + // Take care of loading iconback which can have multiple images + if (mIconBackCount > 0) { + mComposedIconInfo.iconBacks = new int[mIconBackCount]; + for (int i = 0; i < mIconBackCount; i++) { + mComposedIconInfo.iconBacks[i] = + getResourceIdForName( + new ComponentName(String.format(ICON_BACK_FORMAT, i), "")); + } + } + + // Get the icon scale from this pack + String scale = mIconPackResourceMap.get(ICON_SCALE_COMPONENT); + if (scale != null) { + try { + mComposedIconInfo.iconScale = Float.valueOf(scale); + } catch (NumberFormatException e) { + mComposedIconInfo.iconScale = DEFAULT_SCALE; + } + } else { + mComposedIconInfo.iconScale = DEFAULT_SCALE; + } + } + + private int getResourceIdForName(ComponentName component) { + String item = mIconPackResourceMap.get(component); + if (!TextUtils.isEmpty(item)) { + return getResourceIdForDrawable(item); + } + return 0; + } + + public static Resources createIconResource(Context context, String packageName) + throws NameNotFoundException { + PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); + String themeApk = info.applicationInfo.publicSourceDir; + + String prefixPath; + String iconApkPath; + if (info.isLegacyIconPackApk) { + iconApkPath = ""; + prefixPath = ""; + } else { + prefixPath = ThemeUtils.ICONS_PATH; //path inside APK + iconApkPath = ThemeUtils.getIconPackApkPath(packageName); + } + + AssetManager assets = new AssetManager(); + assets.addIconPath(themeApk, iconApkPath, + prefixPath, Resources.THEME_ICON_PKG_ID); + + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + Configuration config = context.getResources().getConfiguration(); + Resources res = new Resources(assets, dm, config); + return res; + } + + public Map getIconResMapFromXml(Resources res, String packageName) { + XmlPullParser parser = null; + InputStream inputStream = null; + Map iconPackResources = new HashMap(); + + try { + inputStream = res.getAssets().open("appfilter.xml"); + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + parser = factory.newPullParser(); + parser.setInput(inputStream, "UTF-8"); + } catch (Exception e) { + // Catch any exception since we want to fall back to parsing the xml/ + // resource in all cases + int resId = res.getIdentifier("appfilter", "xml", packageName); + if (resId != 0) { + parser = res.getXml(resId); + } + } + + if (parser != null) { + try { + loadResourcesFromXmlParser(parser, iconPackResources); + return iconPackResources; + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + // Cleanup resources + if (parser instanceof XmlResourceParser) { + ((XmlResourceParser) parser).close(); + } else if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + } + } + } + } + + // Application uses a different theme format (most likely launcher pro) + int arrayId = res.getIdentifier("theme_iconpack", "array", packageName); + if (arrayId == 0) { + arrayId = res.getIdentifier("icon_pack", "array", packageName); + } + + if (arrayId != 0) { + String[] iconPack = res.getStringArray(arrayId); + ComponentName compName = null; + for (String entry : iconPack) { + + if (TextUtils.isEmpty(entry)) { + continue; + } + + String icon = entry; + entry = entry.replaceAll("_", "."); + + compName = new ComponentName(entry.toLowerCase(), ""); + iconPackResources.put(compName, icon); + + int activityIndex = entry.lastIndexOf("."); + if (activityIndex <= 0 || activityIndex == entry.length() - 1) { + continue; + } + + String iconPackage = entry.substring(0, activityIndex); + if (TextUtils.isEmpty(iconPackage)) { + continue; + } + + String iconActivity = entry.substring(activityIndex + 1); + if (TextUtils.isEmpty(iconActivity)) { + continue; + } + + // Store entries as lower case to ensure match + iconPackage = iconPackage.toLowerCase(); + iconActivity = iconActivity.toLowerCase(); + + iconActivity = iconPackage + "." + iconActivity; + compName = new ComponentName(iconPackage, iconActivity); + iconPackResources.put(compName, icon); + } + } + return iconPackResources; + } + + boolean isIconPackLoaded() { + return mLoadedIconPackResource != null && + mLoadedIconPackName != null && + mIconPackResourceMap != null; + } + + private int getResourceIdForDrawable(String resource) { + int resId = + mLoadedIconPackResource.getIdentifier(resource, "drawable",mLoadedIconPackName); + return resId; + } + + public int getResourceIdForActivityIcon(ActivityInfo info) { + if (!isIconPackLoaded()) { + return 0; + } + ComponentName compName = new ComponentName(info.packageName.toLowerCase(), + info.name.toLowerCase()); + String drawable = mIconPackResourceMap.get(compName); + if (drawable != null) { + int resId = getResourceIdForDrawable(drawable); + if (resId != 0) return resId; + } + + // Icon pack doesn't have an icon for the activity, fallback to package icon + compName = new ComponentName(info.packageName.toLowerCase(), ""); + drawable = mIconPackResourceMap.get(compName); + if (drawable == null) { + return 0; + } + return getResourceIdForDrawable(drawable); + } + + public int getResourceIdForApp(String pkgName) { + ActivityInfo info = new ActivityInfo(); + info.packageName = pkgName; + info.name = ""; + return getResourceIdForActivityIcon(info); + } + + public Drawable getDrawableForActivity(ActivityInfo info) { + int id = getResourceIdForActivityIcon(info); + if (id == 0) return null; + return mLoadedIconPackResource.getDrawable(id, null, false); + } + + public Drawable getDrawableForActivityWithDensity(ActivityInfo info, int density) { + int id = getResourceIdForActivityIcon(info); + if (id == 0) return null; + return mLoadedIconPackResource.getDrawableForDensity(id, density, null, false); + } + + public static boolean shouldComposeIcon(ComposedIconInfo iconInfo) { + return iconInfo != null && + (iconInfo.iconBacks != null || + iconInfo.iconMask != 0 || + iconInfo.iconUpon != 0 || + iconInfo.colorFilter != null || + iconInfo.iconPaletteBack != 0 || + iconInfo.iconRotation != 0 || + iconInfo.iconRotationVariance != 0 || + iconInfo.iconTranslationX != 0 || + iconInfo.iconTranslationY != 0 || + iconInfo.iconScale != 1f); + } + + public static class IconCustomizer { + private static final Random sRandom = new Random(); + private static final IIconCacheManager sIconCacheManager; + + static { + sIconCacheManager = IIconCacheManager.Stub.asInterface( + ServiceManager.getService(ICON_CACHE_SERVICE)); + } + + public static Drawable getComposedIconDrawable(Drawable icon, Context context, + ComposedIconInfo iconInfo) { + final Resources res = context.getResources(); + return getComposedIconDrawable(icon, res, iconInfo); + } + + public static Drawable getComposedIconDrawable(Drawable icon, Resources res, + ComposedIconInfo iconInfo) { + if (iconInfo == null) return icon; + int back = 0; + int defaultSwatchColor = 0; + if (iconInfo.swatchType != ComposedIconInfo.SwatchType.None) { + back = iconInfo.iconPaletteBack; + if (iconInfo.defaultSwatchColors.length > 0) { + defaultSwatchColor = iconInfo.defaultSwatchColors[ + sRandom.nextInt(iconInfo.defaultSwatchColors.length)]; + } + } else if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { + back = iconInfo.iconBacks[sRandom.nextInt(iconInfo.iconBacks.length)]; + } + Bitmap bmp = createIconBitmap(icon, res, back, defaultSwatchColor, iconInfo); + return bmp != null ? new BitmapDrawable(res, bmp): null; + } + + public static void getValue(Resources res, int resId, TypedValue outValue, + Drawable baseIcon) { + final String pkgName = res.getAssets().getAppName(); + final ComposedIconInfo iconInfo = res.getComposedIconInfo(); + if (iconInfo == null) { + // No composed icon info available so return, keeping original value + return; + } + TypedValue tempValue = new TypedValue(); + tempValue.setTo(outValue); + // Catch all exceptions and restore outValue to tempValue if one occurs + try { + outValue.assetCookie = COMPOSED_ICON_COOKIE; + outValue.data = resId & (COMPOSED_ICON_COOKIE << 24 | 0x00ffffff); + outValue.string = getCachedIconPath(pkgName, resId, outValue.density); + int hashCode = outValue.string.hashCode() & 0x7fffffff; + int defaultSwatchColor = 0; + + if (!(new File(outValue.string.toString()).exists())) { + // compose the icon and cache it + int back = 0; + if (iconInfo.swatchType != ComposedIconInfo.SwatchType.None) { + back = iconInfo.iconPaletteBack; + if (iconInfo.defaultSwatchColors.length > 0) { + defaultSwatchColor = iconInfo.defaultSwatchColors[ + hashCode % iconInfo.defaultSwatchColors.length]; + } + } else if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { + back = iconInfo.iconBacks[hashCode % iconInfo.iconBacks.length]; + } + if (DEBUG) { + Log.d(TAG, "Composing icon for " + pkgName); + } + Bitmap bmp = createIconBitmap(baseIcon, res, back, defaultSwatchColor, + iconInfo); + if (!cacheComposedIcon(bmp, + getCachedIconName(pkgName, resId, outValue.density))) { + Log.w(TAG, "Unable to cache icon " + outValue.string); + // restore the original TypedValue + outValue.setTo(tempValue); + } + } + } catch (Exception e) { + // catch all, restore the original value and log it + outValue.setTo(tempValue); + Log.w(TAG, "getValue failed for " + outValue.string, e); + } + } + + private static Bitmap createIconBitmap(Drawable icon, Resources res, int iconBack, + int defaultSwatchColor, ComposedIconInfo iconInfo) { + if (iconInfo.iconSize <= 0) return null; + + final Canvas canvas = new Canvas(); + canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, + Paint.FILTER_BITMAP_FLAG)); + + int width = 0, height = 0; + int backTintColor = 0; + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(iconInfo.iconSize); + painter.setIntrinsicHeight(iconInfo.iconSize); + + // A PaintDrawable does not have an exact size + width = iconInfo.iconSize; + height = iconInfo.iconSize; + } else if (icon instanceof BitmapDrawable) { + // Ensure the bitmap has a density. + BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; + Bitmap bitmap = bitmapDrawable.getBitmap(); + if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { + bitmapDrawable.setTargetDensity(res.getDisplayMetrics()); + } + canvas.setDensity(bitmap.getDensity()); + + // If the original size of the icon isn't greater + // than twice the size of recommended large icons + // respect the original size of the icon + // otherwise enormous icons can easily create + // OOM situations. + if ((bitmap.getWidth() < (iconInfo.iconSize * 2)) + && (bitmap.getHeight() < (iconInfo.iconSize * 2))) { + width = bitmap.getWidth(); + height = bitmap.getHeight(); + } else { + width = iconInfo.iconSize; + height = iconInfo.iconSize; + } + if (iconInfo.swatchType != ComposedIconInfo.SwatchType.None) { + Palette palette = Palette.generate(bitmap, NUM_PALETTE_COLORS); + switch (iconInfo.swatchType) { + case Vibrant: + backTintColor = palette.getVibrantColor(defaultSwatchColor); + break; + case VibrantLight: + backTintColor = palette.getLightVibrantColor(defaultSwatchColor); + break; + case VibrantDark: + backTintColor = palette.getDarkVibrantColor(defaultSwatchColor); + break; + case Muted: + backTintColor = palette.getMutedColor(defaultSwatchColor); + break; + case MutedLight: + backTintColor = palette.getLightMutedColor(defaultSwatchColor); + break; + case MutedDark: + backTintColor = palette.getDarkMutedColor(defaultSwatchColor); + break; + } + if (DEBUG) { + Log.d(TAG, String.format("palette tint color=0x%08x", backTintColor)); + } + } + } else if (icon instanceof VectorDrawable) { + width = height = iconInfo.iconSize; + } + + if (width <= 0 || height <= 0) return null; + + Bitmap bitmap = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + canvas.setBitmap(bitmap); + + // Scale the original + Rect oldBounds = new Rect(); + oldBounds.set(icon.getBounds()); + icon.setBounds(0, 0, width, height); + canvas.save(); + final float halfWidth = width / 2f; + final float halfHeight = height / 2f; + float angle = iconInfo.iconRotation; + if (iconInfo.iconRotationVariance != 0) { + angle += (sRandom.nextFloat() * (iconInfo.iconRotationVariance * 2)) + - iconInfo.iconRotationVariance; + } + canvas.scale(iconInfo.iconScale, iconInfo.iconScale, halfWidth, halfHeight); + canvas.translate(iconInfo.iconTranslationX, iconInfo.iconTranslationY); + canvas.rotate(angle, halfWidth, halfHeight); + if (iconInfo.colorFilter != null) { + Paint p = null; + if (icon instanceof BitmapDrawable) { + p = ((BitmapDrawable) icon).getPaint(); + } else if (icon instanceof PaintDrawable) { + p = ((PaintDrawable) icon).getPaint(); + } + if (p != null) p.setColorFilter(new ColorMatrixColorFilter(iconInfo.colorFilter)); + } + icon.draw(canvas); + canvas.restore(); + + // Mask off the original if iconMask is not null + if (iconInfo.iconMask != 0) { + Drawable mask = res.getDrawable(iconInfo.iconMask); + if (mask != null) { + mask.setBounds(icon.getBounds()); + ((BitmapDrawable) mask).getPaint().setXfermode( + new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + mask.draw(canvas); + } + } + // Draw the iconBacks if not null and then the original (scaled and masked) icon on top + if (iconBack != 0) { + Drawable back = res.getDrawable(iconBack); + if (back != null) { + back.setBounds(icon.getBounds()); + Paint paint = ((BitmapDrawable) back).getPaint(); + paint.setXfermode( + new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); + if (backTintColor != 0) { + paint.setColorFilter(new PorterDuffColorFilter(backTintColor, + PorterDuff.Mode.MULTIPLY)); + } + back.draw(canvas); + } + } + // Finally draw the foreground if one was supplied + if (iconInfo.iconUpon != 0) { + Drawable upon = res.getDrawable(iconInfo.iconUpon); + if (upon != null) { + upon.setBounds(icon.getBounds()); + upon.draw(canvas); + } + } + icon.setBounds(oldBounds); + bitmap.setDensity(canvas.getDensity()); + + return bitmap; + } + + private static boolean cacheComposedIcon(Bitmap bmp, String path) { + try { + return sIconCacheManager.cacheComposedIcon(bmp, path); + } catch (RemoteException e) { + Log.e(TAG, "Unable to cache icon.", e); + } + + return false; + } + + private static String getCachedIconPath(String pkgName, int resId, int density) { + return String.format("%s/%s", SYSTEM_THEME_ICON_CACHE_DIR, + getCachedIconName(pkgName, resId, density)); + } + + private static String getCachedIconName(String pkgName, int resId, int density) { + return String.format("%s_%08x_%d.png", pkgName, resId, density); + } + } + + public static class ColorFilterUtils { + private static final String TAG_FILTER = "filter"; + private static final String FILTER_HUE = "hue"; + private static final String FILTER_SATURATION = "saturation"; + private static final String FILTER_INVERT = "invert"; + private static final String FILTER_BRIGHTNESS = "brightness"; + private static final String FILTER_CONTRAST = "contrast"; + private static final String FILTER_ALPHA = "alpha"; + private static final String FILTER_TINT = "tint"; + + private static final int MIN_HUE = -180; + private static final int MAX_HUE = 180; + private static final int MIN_SATURATION = 0; + private static final int MAX_SATURATION = 200; + private static final int MIN_BRIGHTNESS = 0; + private static final int MAX_BRIGHTNESS = 200; + private static final int MIN_CONTRAST = -100; + private static final int MAX_CONTRAST = 100; + private static final int MIN_ALPHA = 0; + private static final int MAX_ALPHA = 100; + + public static boolean parseIconFilter(XmlPullParser parser, Builder builder) + throws IOException, XmlPullParserException { + String tag = parser.getName(); + if (!TAG_FILTER.equals(tag)) return false; + + int attrCount = parser.getAttributeCount(); + String attrName; + String attr = null; + int intValue; + while (attrCount-- > 0) { + attrName = parser.getAttributeName(attrCount); + if (attrName.equals("name")) { + attr = parser.getAttributeValue(attrCount); + } + } + String content = parser.nextText(); + if (attr != null && content != null && content.length() > 0) { + content = content.trim(); + if (FILTER_HUE.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 0),MIN_HUE, MAX_HUE); + builder.hue(intValue); + } else if (FILTER_SATURATION.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), + MIN_SATURATION, MAX_SATURATION); + builder.saturate(intValue); + } else if (FILTER_INVERT.equalsIgnoreCase(attr)) { + if ("true".equalsIgnoreCase(content)) { + builder.invertColors(); + } + } else if (FILTER_BRIGHTNESS.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + builder.brightness(intValue); + } else if (FILTER_CONTRAST.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 0), + MIN_CONTRAST, MAX_CONTRAST); + builder.contrast(intValue); + } else if (FILTER_ALPHA.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), MIN_ALPHA, MAX_ALPHA); + builder.alpha(intValue); + } else if (FILTER_TINT.equalsIgnoreCase(attr)) { + try { + intValue = Color.parseColor(content); + builder.tint(intValue); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Cannot apply tint, invalid argument: " + content); + } + } + } + return true; + } + + private static int getInt(String value, int defaultValue) { + try { + return Integer.valueOf(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + private static int clampValue(int value, int min, int max) { + return Math.min(max, Math.max(min, value)); + } + + /** + * See the following links for reference + * http://groups.google.com/group/android-developers/browse_thread/thread/9e215c83c3819953 + * http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html + * @param value + */ + public static ColorMatrix adjustHue(float value) { + ColorMatrix cm = new ColorMatrix(); + value = value / 180 * (float) Math.PI; + if (value != 0) { + float cosVal = (float) Math.cos(value); + float sinVal = (float) Math.sin(value); + float lumR = 0.213f; + float lumG = 0.715f; + float lumB = 0.072f; + float[] mat = new float[]{ + lumR + cosVal * (1 - lumR) + sinVal * (-lumR), + lumG + cosVal * (-lumG) + sinVal * (-lumG), + lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0, + lumR + cosVal * (-lumR) + sinVal * (0.143f), + lumG + cosVal * (1 - lumG) + sinVal * (0.140f), + lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0, + lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), + lumG + cosVal * (-lumG) + sinVal * (lumG), + lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0, + 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1}; + cm.set(mat); + } + return cm; + } + + public static ColorMatrix adjustSaturation(float saturation) { + saturation = saturation / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setSaturation(saturation); + + return cm; + } + + public static ColorMatrix invertColors() { + float[] matrix = { + -1, 0, 0, 0, 255, //red + 0, -1, 0, 0, 255, //green + 0, 0, -1, 0, 255, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static ColorMatrix adjustBrightness(float brightness) { + brightness = brightness / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setScale(brightness, brightness, brightness, 1); + + return cm; + } + + public static ColorMatrix adjustContrast(float contrast) { + contrast = contrast / 100 + 1; + float o = (-0.5f * contrast + 0.5f) * 255; + float[] matrix = { + contrast, 0, 0, 0, o, //red + 0, contrast, 0, 0, o, //green + 0, 0, contrast, 0, o, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static ColorMatrix adjustAlpha(float alpha) { + alpha = alpha / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setScale(1, 1, 1, alpha); + + return cm; + } + + public static ColorMatrix applyTint(int color) { + float alpha = Color.alpha(color) / 255f; + float red = Color.red(color) * alpha; + float green = Color.green(color) * alpha; + float blue = Color.blue(color) * alpha; + + float[] matrix = { + 1, 0, 0, 0, red, //red + 0, 1, 0, 0, green, //green + 0, 0, 1, 0, blue, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static class Builder { + private List mMatrixList; + + public Builder() { + mMatrixList = new ArrayList(); + } + + public Builder hue(float value) { + mMatrixList.add(adjustHue(value)); + return this; + } + + public Builder saturate(float saturation) { + mMatrixList.add(adjustSaturation(saturation)); + return this; + } + + public Builder brightness(float brightness) { + mMatrixList.add(adjustBrightness(brightness)); + return this; + } + + public Builder contrast(float contrast) { + mMatrixList.add(adjustContrast(contrast)); + return this; + } + + public Builder alpha(float alpha) { + mMatrixList.add(adjustAlpha(alpha)); + return this; + } + + public Builder invertColors() { + mMatrixList.add(ColorFilterUtils.invertColors()); + return this; + } + + public Builder tint(int color) { + mMatrixList.add(applyTint(color)); + return this; + } + + public ColorMatrix build() { + if (mMatrixList == null || mMatrixList.size() == 0) return null; + + ColorMatrix colorMatrix = new ColorMatrix(); + for (ColorMatrix cm : mMatrixList) { + colorMatrix.postConcat(cm); + } + return colorMatrix; + } + } + } +} diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 69b8b9510b3a2..cddcd9fe45927 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -374,6 +374,7 @@ public void runOnMainSync(Runnable runner) { * @see Context#startActivity */ public Activity startActivitySync(Intent intent) { + android.util.SeempLog.record_str(376, intent.toString()); validateNotAppThread(); synchronized (mSync) { @@ -1481,6 +1482,7 @@ public Intent getResultData() { public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { + android.util.SeempLog.record_str(377, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; Uri referrer = target != null ? target.onProvideReferrer() : null; if (referrer != null) { @@ -1541,6 +1543,7 @@ public void execStartActivities(Context who, IBinder contextThread, public void execStartActivitiesAsUser(Context who, IBinder contextThread, IBinder token, Activity target, Intent[] intents, Bundle options, int userId) { + android.util.SeempLog.record_str(378, intents.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1604,6 +1607,7 @@ public void execStartActivitiesAsUser(Context who, IBinder contextThread, public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options) { + android.util.SeempLog.record_str(377, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1664,6 +1668,7 @@ public ActivityResult execStartActivity( public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, UserHandle user) { + android.util.SeempLog.record_str(377, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1703,6 +1708,7 @@ public ActivityResult execStartActivityAsCaller( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity, int userId) { + android.util.SeempLog.record_str(379, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1741,6 +1747,7 @@ public ActivityResult execStartActivityAsCaller( public void execStartActivityFromAppTask( Context who, IBinder contextThread, IAppTask appTask, Intent intent, Bundle options) { + android.util.SeempLog.record_str(380, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index c2bf28a931c8e..76e55b7e3c833 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -546,7 +546,8 @@ public AssetManager getAssets(ActivityThread mainThread) { public Resources getResources(ActivityThread mainThread) { if (mResources == null) { mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs, - mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this); + mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this, + mainThread.getSystemContext(), mPackageName); } return mResources; } @@ -601,7 +602,7 @@ public Application makeApplication(boolean forceDefaultAppClass, final int N = packageIdentifiers.size(); for (int i = 0; i < N; i++) { final int id = packageIdentifiers.keyAt(i); - if (id == 0x01 || id == 0x7f) { + if (id == 0x01 || id == 0x7f || id == 0x3f) { continue; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 014c069a7da84..8835a090f09fe 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -522,6 +522,21 @@ public class Notification implements Parcelable @Priority public int priority; + /** + * Default. + * Show all notifications from an app on keyguard. + * + * @hide + */ + public static final int SHOW_ALL_NOTI_ON_KEYGUARD = 0x01; + + /** + * Show only notifications from an app which are not ongoing ones. + * + * @hide + */ + public static final int SHOW_NO_ONGOING_NOTI_ON_KEYGUARD = 0x02; + /** * Accent color (an ARGB integer like the constants in {@link android.graphics.Color}) * to be applied by the standard Style templates when presenting this notification. @@ -897,6 +912,13 @@ public String getSortKey() { private Icon mSmallIcon; private Icon mLargeIcon; + /** + * Used by light picker in Settings to force + * notification lights on when screen is on + * @hide + */ + public static final String EXTRA_FORCE_SHOW_LIGHTS = "android.forceShowLights"; + /** * Structure to encapsulate a named action that can be shown as part of this notification. * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is diff --git a/core/java/android/app/NotificationGroup.aidl b/core/java/android/app/NotificationGroup.aidl new file mode 100644 index 0000000000000..44b62907fb42a --- /dev/null +++ b/core/java/android/app/NotificationGroup.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2012, The CyanogenMod 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; + +parcelable NotificationGroup; diff --git a/core/java/android/app/NotificationGroup.java b/core/java/android/app/NotificationGroup.java new file mode 100644 index 0000000000000..bcb70d31749e1 --- /dev/null +++ b/core/java/android/app/NotificationGroup.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2011 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 org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** @hide */ +public class NotificationGroup implements Parcelable { + private static final String TAG = "NotificationGroup"; + + private String mName; + private int mNameResId; + + private UUID mUuid; + + private Set mPackages = new HashSet(); + + private boolean mDirty; + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public NotificationGroup createFromParcel(Parcel in) { + return new NotificationGroup(in); + } + + @Override + public NotificationGroup[] newArray(int size) { + return new NotificationGroup[size]; + } + }; + + public NotificationGroup(String name) { + this(name, -1, null); + } + + public NotificationGroup(String name, int nameResId, UUID uuid) { + mName = name; + mNameResId = nameResId; + mUuid = (uuid != null) ? uuid : UUID.randomUUID(); + mDirty = uuid == null; + } + + private NotificationGroup(Parcel in) { + readFromParcel(in); + } + + @Override + public String toString() { + return getName(); + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + mNameResId = -1; + mDirty = true; + } + + public UUID getUuid() { + return mUuid; + } + + public void addPackage(String pkg) { + mPackages.add(pkg); + mDirty = true; + } + + public String[] getPackages() { + return mPackages.toArray(new String[mPackages.size()]); + } + + public void removePackage(String pkg) { + mPackages.remove(pkg); + mDirty = true; + } + + public boolean hasPackage(String pkg) { + return mPackages.contains(pkg); + } + + public boolean isDirty() { + return mDirty; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mName); + dest.writeInt(mNameResId); + dest.writeInt(mDirty ? 1 : 0); + new ParcelUuid(mUuid).writeToParcel(dest, 0); + dest.writeStringArray(getPackages()); + } + + public void readFromParcel(Parcel in) { + mName = in.readString(); + mNameResId = in.readInt(); + mDirty = in.readInt() != 0; + mUuid = ParcelUuid.CREATOR.createFromParcel(in).getUuid(); + mPackages.addAll(Arrays.asList(in.readStringArray())); + } + + public void getXmlString(StringBuilder builder, Context context) { + builder.append(" 0) { + builder.append("nameres=\""); + builder.append(context.getResources().getResourceEntryName(mNameResId)); + } else { + builder.append("name=\""); + builder.append(TextUtils.htmlEncode(getName())); + } + builder.append("\" uuid=\""); + builder.append(TextUtils.htmlEncode(getUuid().toString())); + builder.append("\">\n"); + for (String pkg : mPackages) { + builder.append("" + TextUtils.htmlEncode(pkg) + "\n"); + } + builder.append("\n"); + mDirty = false; + } + + public static NotificationGroup fromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, IOException { + String value = xpp.getAttributeValue(null, "nameres"); + int nameResId = -1; + String name = null; + UUID uuid = null; + + if (value != null) { + nameResId = context.getResources().getIdentifier(value, "string", "android"); + if (nameResId > 0) { + name = context.getResources().getString(nameResId); + } + } + + if (name == null) { + name = xpp.getAttributeValue(null, "name"); + } + + value = xpp.getAttributeValue(null, "uuid"); + if (value != null) { + try { + uuid = UUID.fromString(value); + } catch (IllegalArgumentException e) { + Log.w(TAG, "UUID not recognized for " + name + ", using new one."); + } + } + + NotificationGroup notificationGroup = new NotificationGroup(name, nameResId, uuid); + int event = xpp.next(); + while (event != XmlPullParser.END_TAG || !xpp.getName().equals("notificationGroup")) { + if (event == XmlPullParser.START_TAG) { + if (xpp.getName().equals("package")) { + String pkg = xpp.nextText(); + notificationGroup.addPackage(pkg); + } + } + event = xpp.next(); + } + + /* we just loaded from XML, no need to save */ + notificationGroup.mDirty = false; + + return notificationGroup; + } +} diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 605c006130878..eae2599c358d6 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -19,6 +19,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.app.Notification; import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; @@ -340,12 +341,12 @@ public ComponentName getEffectsSuppressor() { /** * @hide */ - public boolean matchesCallFilter(Bundle extras) { + public boolean[] matchesCallFilter(Bundle extras) { INotificationManager service = getService(); try { return service.matchesCallFilter(extras); } catch (RemoteException e) { - return false; + return null; } } @@ -512,6 +513,16 @@ public ArraySet getPackagesRequestingNotificationPolicyAccess() { return new ArraySet(); } + /** @hide */ + public int getShowNotificationForPackageOnKeyguard(String pkg, int uid) { + INotificationManager service = getService(); + try { + return getService().getShowNotificationForPackageOnKeyguard(pkg, uid); + } catch (RemoteException e) { + return Notification.SHOW_ALL_NOTI_ON_KEYGUARD; + } + } + private Context mContext; private static void checkRequired(String name, Object value) { @@ -754,4 +765,32 @@ public static int zenModeFromInterruptionFilter(int interruptionFilter, int defV default: return defValue; } } + + /** @hide */ + public static final int LIGHTS_RGB_NOTIFICATION = 0; + /** @hide */ + public static final int LIGHTS_RGB_BATTERY = 1 ; + /** @hide */ + public static final int LIGHTS_MULTIPLE_LED = 2; + /** @hide */ + public static final int LIGHTS_LED_PULSE = 3; + /** @hide */ + public static final int LIGHTS_SEGMENTED_BATTERY_LIGHTS = 4; + /** @hide */ + public static final int LIGHTS_ADJUSTABLE_NOTIFICATION_BRIGHTNESS = 5; + + /** @hide */ + public boolean deviceLightsCan(int lightCapability) { + INotificationManager service = getService(); + try { + return service.deviceLightsCan(lightCapability); + } catch (RemoteException e) { + return true; + } catch (NullPointerException e) { + return true; + } + // If the service isn't up yet, assume everything is possible + } + + } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 211759735339a..c8aec2e1d109a 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -18,22 +18,38 @@ import static android.app.ActivityThread.DEBUG_CONFIGURATION; +import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.ThemeUtils; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.content.res.ThemeConfig; import android.content.res.Resources; import android.content.res.ResourcesKey; +import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; import android.view.Display; import android.view.DisplayAdjustments; import java.lang.ref.WeakReference; +import java.util.List; import java.util.Locale; /** @hide */ @@ -48,9 +64,16 @@ public class ResourcesManager { new ArrayMap<>(); CompatibilityInfo mResCompatibilityInfo; + static IPackageManager sPackageManager; Configuration mResConfiguration; + /** + * Number of default assets attached to a Resource object's AssetManager + * This currently includes framework and cmsdk resources + */ + private static final int NUM_DEFAULT_ASSETS = 2; + public static ResourcesManager getInstance() { synchronized (ResourcesManager.class) { if (sResourcesManager == null) { @@ -156,12 +179,15 @@ public Display getAdjustedDisplay(final int displayId, DisplayAdjustments displa * @param compatInfo the compatibility info. Must not be null. */ Resources getTopLevelResources(String resDir, String[] splitResDirs, - String[] overlayDirs, String[] libDirs, int displayId, - Configuration overrideConfiguration, CompatibilityInfo compatInfo) { + String[] overlayDirs, String[] libDirs, int displayId, String packageName, + Configuration overrideConfiguration, CompatibilityInfo compatInfo, Context context, + boolean isThemeable) { final float scale = compatInfo.applicationScale; + ThemeConfig themeConfig = getThemeConfig(); Configuration overrideConfigCopy = (overrideConfiguration != null) ? new Configuration(overrideConfiguration) : null; - ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale); + ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, + isThemeable, getThemeConfig()); Resources r; synchronized (this) { // Resources is app scale dependent. @@ -184,6 +210,8 @@ Resources getTopLevelResources(String resDir, String[] splitResDirs, //} AssetManager assets = new AssetManager(); + assets.setAppName(packageName); + assets.setThemeSupport(isThemeable); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. @@ -203,7 +231,7 @@ Resources getTopLevelResources(String resDir, String[] splitResDirs, if (overlayDirs != null) { for (String idmapPath : overlayDirs) { - assets.addOverlayPath(idmapPath); + assets.addOverlayPath(idmapPath, null, null, null, null); } } @@ -213,7 +241,7 @@ Resources getTopLevelResources(String resDir, String[] splitResDirs, // Avoid opening files we know do not have resources, // like code-only .jar files. if (assets.addAssetPath(libDir) == 0) { - Log.w(TAG, "Asset path '" + libDir + + Slog.w(TAG, "Asset path '" + libDir + "' does not exist or contains no resources."); } } @@ -237,10 +265,38 @@ Resources getTopLevelResources(String resDir, String[] splitResDirs, } else { config = getConfiguration(); } + + boolean iconsAttached = false; + /* Attach theme information to the resulting AssetManager when appropriate. */ + if (config != null && !context.getPackageManager().isSafeMode()) { + if (themeConfig == null) { + try { + themeConfig = ThemeConfig.getBootTheme(context.getContentResolver()); + } catch (Exception e) { + Slog.d(TAG, "ThemeConfig.getBootTheme failed, falling back to system theme", e); + themeConfig = ThemeConfig.getSystemTheme(); + } + } + + if (isThemeable) { + if (themeConfig != null) { + attachThemeAssets(assets, themeConfig); + attachCommonAssets(assets, themeConfig); + iconsAttached = attachIconAssets(assets, themeConfig); + } + } else if (themeConfig != null && + !ThemeConfig.SYSTEM_DEFAULT.equals(themeConfig.getFontPkgName())) { + // use system fonts if not themeable and a theme font is currently in use + Typeface.recreateDefaults(true); + } + } + r = new Resources(assets, dm, config, compatInfo); + if (iconsAttached) setActivityIcons(r); if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale); + synchronized (this) { WeakReference wr = mActiveResources.get(key); Resources existing = wr != null ? wr.get() : null; @@ -258,6 +314,127 @@ Resources getTopLevelResources(String resDir, String[] splitResDirs, } } + /** + * Creates the top level Resources for applications with the given compatibility info. + * + * @param resDir the resource directory. + * @param compatInfo the compability info. Must not be null. + * + * @hide + */ + public Resources getTopLevelThemedResources(String resDir, int displayId, String packageName, + String themePackageName, CompatibilityInfo compatInfo, boolean isThemeable) { + Resources r; + + ThemeConfig.Builder builder = new ThemeConfig.Builder(); + builder.defaultOverlay(themePackageName); + builder.defaultIcon(themePackageName); + builder.defaultFont(themePackageName); + ThemeConfig themeConfig = builder.build(); + + ResourcesKey key = new ResourcesKey(resDir, displayId, null, compatInfo.applicationScale, + isThemeable, themeConfig); + + AssetManager assets = new AssetManager(); + assets.setAppName(packageName); + assets.setThemeSupport(isThemeable); + if (assets.addAssetPath(resDir) == 0) { + return null; + } + + //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); + DisplayMetrics dm = getDisplayMetricsLocked(displayId); + Configuration config; + boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); + final boolean hasOverrideConfig = key.hasOverrideConfiguration(); + if (!isDefaultDisplay || hasOverrideConfig) { + config = new Configuration(getConfiguration()); + if (!isDefaultDisplay) { + applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config); + } + if (hasOverrideConfig) { + config.updateFrom(key.mOverrideConfiguration); + } + } else { + config = getConfiguration(); + } + + boolean iconsAttached = false; + if (isThemeable) { + /* Attach theme information to the resulting AssetManager when appropriate. */ + attachThemeAssets(assets, themeConfig); + attachCommonAssets(assets, themeConfig); + iconsAttached = attachIconAssets(assets, themeConfig); + } + r = new Resources(assets, dm, config, compatInfo); + if (iconsAttached) setActivityIcons(r); + + if (false) { + Slog.i(TAG, "Created THEMED app resources " + resDir + " " + r + ": " + + r.getConfiguration() + " appScale=" + + r.getCompatibilityInfo().applicationScale); + } + + return r; + } + + /** + * Creates a map between an activity & app's icon ids to its component info. This map + * is then stored in the resource object. + * When resource.getDrawable(id) is called it will check this mapping and replace + * the id with the themed resource id if one is available + * @param r + */ + private void setActivityIcons(Resources r) { + SparseArray iconResources = new SparseArray(); + String pkgName = r.getAssets().getAppName(); + PackageInfo pkgInfo = null; + ApplicationInfo appInfo = null; + + try { + pkgInfo = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES, + UserHandle.getCallingUserId()); + } catch (RemoteException e1) { + Slog.e(TAG, "Unable to get pkg " + pkgName, e1); + return; + } + + final ThemeConfig themeConfig = r.getConfiguration().themeConfig; + if (pkgName != null && themeConfig != null && + pkgName.equals(themeConfig.getIconPackPkgName())) { + return; + } + + //Map application icon + if (pkgInfo != null && pkgInfo.applicationInfo != null) { + appInfo = pkgInfo.applicationInfo; + if (appInfo.themedIcon != 0 || iconResources.get(appInfo.icon) == null) { + iconResources.put(appInfo.icon, appInfo); + } + } + + //Map activity icons. + if (pkgInfo != null && pkgInfo.activities != null) { + for (ActivityInfo ai : pkgInfo.activities) { + if (ai.icon != 0 && (ai.themedIcon != 0 || iconResources.get(ai.icon) == null)) { + iconResources.put(ai.icon, ai); + } else if (appInfo != null && appInfo.icon != 0 && + (ai.themedIcon != 0 || iconResources.get(appInfo.icon) == null)) { + iconResources.put(appInfo.icon, ai); + } + } + } + + r.setIconResources(iconResources); + final IPackageManager pm = getPackageManager(); + try { + ComposedIconInfo iconInfo = pm.getComposedIconInfo(); + r.setComposedIconInfo(iconInfo); + } catch (Exception e) { + Slog.wtf(TAG, "Failed to retrieve ComposedIconInfo", e); + } + } + final boolean applyConfigurationToResourcesLocked(Configuration config, CompatibilityInfo compat) { if (mResConfiguration == null) { @@ -303,6 +480,22 @@ final boolean applyConfigurationToResourcesLocked(Configuration config, boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); DisplayMetrics dm = defaultDisplayMetrics; final boolean hasOverrideConfiguration = key.hasOverrideConfiguration(); + boolean themeChanged = (changes & ActivityInfo.CONFIG_THEME_RESOURCE) != 0; + if (themeChanged) { + AssetManager am = r.getAssets(); + if (am.hasThemeSupport()) { + r.setIconResources(null); + r.setComposedIconInfo(null); + detachThemeAssets(am); + if (config.themeConfig != null) { + attachThemeAssets(am, config.themeConfig); + attachCommonAssets(am, config.themeConfig); + if (attachIconAssets(am, config.themeConfig)) { + setActivityIcons(r); + } + } + } + } if (!isDefaultDisplay || hasOverrideConfiguration) { if (tmpConfig == null) { tmpConfig = new Configuration(); @@ -319,6 +512,9 @@ final boolean applyConfigurationToResourcesLocked(Configuration config, } else { r.updateConfiguration(config, dm, compat); } + if (themeChanged) { + r.updateStringCache(); + } //Slog.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); } else { @@ -330,4 +526,265 @@ final boolean applyConfigurationToResourcesLocked(Configuration config, return changes != 0; } + public static IPackageManager getPackageManager() { + if (sPackageManager != null) { + return sPackageManager; + } + IBinder b = ServiceManager.getService("package"); + sPackageManager = IPackageManager.Stub.asInterface(b); + return sPackageManager; + } + + + /** + * Attach the necessary theme asset paths and meta information to convert an + * AssetManager to being globally "theme-aware". + * + * @param assets + * @param theme + * @return true if the AssetManager is now theme-aware; false otherwise. + * This can fail, for example, if the theme package has been been + * removed and the theme manager has yet to revert formally back to + * the framework default. + */ + private boolean attachThemeAssets(AssetManager assets, ThemeConfig theme) { + PackageInfo piTheme = null; + PackageInfo piTarget = null; + PackageInfo piAndroid = null; + PackageInfo piCm = null; + + // Some apps run in process of another app (eg keyguard/systemUI) so we must get the + // package name from the res tables. The 0th base package name will be the android group. + // The 1st base package name will be the app group if one is attached. Check if it is there + // first or else the system will crash! + String basePackageName = null; + String resourcePackageName = null; + int count = assets.getBasePackageCount(); + if (count > NUM_DEFAULT_ASSETS) { + basePackageName = assets.getBasePackageName(NUM_DEFAULT_ASSETS); + resourcePackageName = assets.getBaseResourcePackageName(NUM_DEFAULT_ASSETS); + } else if (count == NUM_DEFAULT_ASSETS) { + basePackageName = assets.getBasePackageName(0); + } else { + return false; + } + + try { + piTheme = getPackageManager().getPackageInfo( + theme.getOverlayPkgNameForApp(basePackageName), 0, + UserHandle.getCallingUserId()); + piTarget = getPackageManager().getPackageInfo( + basePackageName, 0, UserHandle.getCallingUserId()); + + // Handle special case where a system app (ex trebuchet) may have had its pkg name + // renamed during an upgrade. basePackageName would be the manifest value which will + // fail on getPackageInfo(). resource pkg is assumed to have the original name + if (piTarget == null && resourcePackageName != null) { + piTarget = getPackageManager().getPackageInfo(resourcePackageName, + 0, UserHandle.getCallingUserId()); + } + piAndroid = getPackageManager().getPackageInfo("android", 0, + UserHandle.getCallingUserId()); + piCm = getPackageManager().getPackageInfo("cyanogenmod.platform", 0, + UserHandle.getCallingUserId()); + } catch (RemoteException e) { + } + + if (piTheme == null || piTheme.applicationInfo == null || + piTarget == null || piTarget.applicationInfo == null || + piAndroid == null || piAndroid.applicationInfo == null || + piCm == null || piCm.applicationInfo == null || + piTheme.mOverlayTargets == null) { + return false; + } + + // Attach themed resources for target + String themePackageName = piTheme.packageName; + String themePath = piTheme.applicationInfo.publicSourceDir; + if (!piTarget.isThemeApk && piTheme.mOverlayTargets.contains(basePackageName)) { + String targetPackagePath = piTarget.applicationInfo.sourceDir; + String prefixPath = ThemeUtils.getOverlayPathToTarget(basePackageName); + + String resCachePath = ThemeUtils.getTargetCacheDir(piTarget.packageName, + piTheme.packageName); + String resApkPath = resCachePath + "/resources.apk"; + String idmapPath = ThemeUtils.getIdmapPath(piTarget.packageName, piTheme.packageName); + int cookie = assets.addOverlayPath(idmapPath, themePath, resApkPath, + targetPackagePath, prefixPath); + + if (cookie != 0) { + assets.setThemePackageName(themePackageName); + assets.addThemeCookie(cookie); + } + } + + // Attach themed resources for cmsdk + if (!piTarget.isThemeApk && !piCm.packageName.equals(basePackageName) && + piTheme.mOverlayTargets.contains(piCm.packageName)) { + String resCachePath= ThemeUtils.getTargetCacheDir(piCm.packageName, + piTheme.packageName); + String prefixPath = ThemeUtils.getOverlayPathToTarget(piCm.packageName); + String targetPackagePath = piCm.applicationInfo.publicSourceDir; + String resApkPath = resCachePath + "/resources.apk"; + String idmapPath = ThemeUtils.getIdmapPath(piCm.packageName, piTheme.packageName); + int cookie = assets.addOverlayPath(idmapPath, themePath, + resApkPath, targetPackagePath, prefixPath); + if (cookie != 0) { + assets.setThemePackageName(themePackageName); + assets.addThemeCookie(cookie); + } + } + + // Attach themed resources for android framework + if (!piTarget.isThemeApk && !"android".equals(basePackageName) && + piTheme.mOverlayTargets.contains("android")) { + String resCachePath= ThemeUtils.getTargetCacheDir(piAndroid.packageName, + piTheme.packageName); + String prefixPath = ThemeUtils.getOverlayPathToTarget(piAndroid.packageName); + String targetPackagePath = piAndroid.applicationInfo.publicSourceDir; + String resApkPath = resCachePath + "/resources.apk"; + String idmapPath = ThemeUtils.getIdmapPath("android", piTheme.packageName); + int cookie = assets.addOverlayPath(idmapPath, themePath, + resApkPath, targetPackagePath, prefixPath); + if (cookie != 0) { + assets.setThemePackageName(themePackageName); + assets.addThemeCookie(cookie); + } + } + + return true; + } + + /** + * Attach the necessary icon asset paths. Icon assets should be in a different + * namespace than the standard 0x7F. + * + * @param assets + * @param theme + * @return true if succes, false otherwise + */ + private boolean attachIconAssets(AssetManager assets, ThemeConfig theme) { + PackageInfo piIcon = null; + try { + piIcon = getPackageManager().getPackageInfo(theme.getIconPackPkgName(), 0, + UserHandle.getCallingUserId()); + } catch (RemoteException e) { + } + + if (piIcon == null || piIcon.applicationInfo == null) { + return false; + } + + String iconPkg = theme.getIconPackPkgName(); + if (iconPkg != null && !iconPkg.isEmpty()) { + String themeIconPath = piIcon.applicationInfo.publicSourceDir; + String prefixPath = ThemeUtils.ICONS_PATH; + String iconDir = ThemeUtils.getIconPackDir(iconPkg); + String resTablePath = iconDir + "/resources.arsc"; + String resApkPath = iconDir + "/resources.apk"; + + // Legacy Icon packs have everything in their APK + if (piIcon.isLegacyIconPackApk) { + prefixPath = ""; + resApkPath = ""; + resTablePath = ""; + } + + int cookie = assets.addIconPath(themeIconPath, resApkPath, prefixPath, + Resources.THEME_ICON_PKG_ID); + if (cookie != 0) { + assets.setIconPackCookie(cookie); + assets.setIconPackageName(iconPkg); + } + } + + return true; + } + + /** + * Attach the necessary common asset paths. Common assets should be in a different + * namespace than the standard 0x7F. + * + * @param assets + * @param theme + * @return true if succes, false otherwise + */ + private boolean attachCommonAssets(AssetManager assets, ThemeConfig theme) { + // Some apps run in process of another app (eg keyguard/systemUI) so we must get the + // package name from the res tables. The 0th base package name will be the android group. + // The 1st base package name will be the app group if one is attached. Check if it is there + // first or else the system will crash! + String basePackageName; + int count = assets.getBasePackageCount(); + if (count > NUM_DEFAULT_ASSETS) { + basePackageName = assets.getBasePackageName(NUM_DEFAULT_ASSETS); + } else if (count == NUM_DEFAULT_ASSETS) { + basePackageName = assets.getBasePackageName(0); + } else { + return false; + } + + PackageInfo piTheme = null; + try { + piTheme = getPackageManager().getPackageInfo( + theme.getOverlayPkgNameForApp(basePackageName), 0, + UserHandle.getCallingUserId()); + } catch (RemoteException e) { + } + + if (piTheme == null || piTheme.applicationInfo == null) { + return false; + } + + String themePackageName = + ThemeUtils.getCommonPackageName(piTheme.applicationInfo.packageName); + if (themePackageName != null && !themePackageName.isEmpty()) { + String themePath = piTheme.applicationInfo.publicSourceDir; + String prefixPath = ThemeUtils.COMMON_RES_PATH; + String resCachePath = + ThemeUtils.getTargetCacheDir(ThemeUtils.COMMON_RES_TARGET, piTheme.packageName); + String resApkPath = resCachePath + "/resources.apk"; + int cookie = assets.addCommonOverlayPath(themePath, resApkPath, + prefixPath); + if (cookie != 0) { + assets.setCommonResCookie(cookie); + assets.setCommonResPackageName(themePackageName); + } + } + + return true; + } + + private void detachThemeAssets(AssetManager assets) { + String themePackageName = assets.getThemePackageName(); + String iconPackageName = assets.getIconPackageName(); + String commonResPackageName = assets.getCommonResPackageName(); + + //Remove Icon pack if it exists + if (!TextUtils.isEmpty(iconPackageName) && assets.getIconPackCookie() > 0) { + assets.removeOverlayPath(iconPackageName, assets.getIconPackCookie()); + assets.setIconPackageName(null); + assets.setIconPackCookie(0); + } + //Remove common resources if it exists + if (!TextUtils.isEmpty(commonResPackageName) && assets.getCommonResCookie() > 0) { + assets.removeOverlayPath(commonResPackageName, assets.getCommonResCookie()); + assets.setCommonResPackageName(null); + assets.setCommonResCookie(0); + } + final List themeCookies = assets.getThemeCookies(); + if (!TextUtils.isEmpty(themePackageName) && !themeCookies.isEmpty()) { + // remove overlays in reverse order + for (int i = themeCookies.size() - 1; i >= 0; i--) { + assets.removeOverlayPath(themePackageName, themeCookies.get(i)); + } + } + assets.getThemeCookies().clear(); + assets.setThemePackageName(null); + } + + private ThemeConfig getThemeConfig() { + final Configuration config = getConfiguration(); + return config != null ? config.themeConfig : null; + } } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 5e8ad68957b2b..fad3f62a8f949 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -92,6 +92,7 @@ public class StatusBarManager { public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0; public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1; + public static final int CAMERA_LAUNCH_SOURCE_SCREEN_GESTURE = 2; private Context mContext; private IStatusBarService mService; diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 3d264c6f2cba9..34c967f96faa2 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -254,7 +254,9 @@ public DownloadManager createService(ContextImpl ctx) { new StaticServiceFetcher() { @Override public BatteryManager createService() { - return new BatteryManager(); + IBinder b = ServiceManager.getService(Context.BATTERY_SERVICE); + IBatteryService service = IBatteryService.Stub.asInterface(b); + return new BatteryManager(service); }}); registerService(Context.NFC_SERVICE, NfcManager.class, diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 22e79b6da220e..045ee39028c7f 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -33,6 +33,7 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; @@ -230,9 +231,10 @@ static class Globals extends IWallpaperManagerCallback.Stub { private IWallpaperManager mService; private Bitmap mWallpaper; private Bitmap mDefaultWallpaper; + private Bitmap mKeyguardWallpaper; private static final int MSG_CLEAR_WALLPAPER = 1; - + Globals(Looper looper) { IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); mService = IWallpaperManager.Stub.asInterface(b); @@ -250,6 +252,12 @@ public void onWallpaperChanged() { } } + public void onKeyguardWallpaperChanged() { + synchronized (this) { + mKeyguardWallpaper = null; + } + } + public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) { synchronized (this) { if (mService != null) { @@ -285,6 +293,23 @@ public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) { } } + /** + * @hide + */ + public Bitmap peekKeyguardWallpaperBitmap(Context context) { + synchronized (this) { + if (mKeyguardWallpaper != null) { + return mKeyguardWallpaper; + } + try { + mKeyguardWallpaper = getCurrentKeyguardWallpaperLocked(context); + } catch (OutOfMemoryError e) { + Log.w(TAG, "No memory load current keyguard wallpaper", e); + } + return mKeyguardWallpaper; + } + } + public void forgetLoadedWallpaper() { synchronized (this) { mWallpaper = null; @@ -292,6 +317,12 @@ public void forgetLoadedWallpaper() { } } + public void forgetLoadedKeyguardWallpaper() { + synchronized (this) { + mKeyguardWallpaper = null; + } + } + private Bitmap getCurrentWallpaperLocked(Context context) { if (mService == null) { Log.w(TAG, "WallpaperService not running"); @@ -321,7 +352,37 @@ private Bitmap getCurrentWallpaperLocked(Context context) { } return null; } - + + private Bitmap getCurrentKeyguardWallpaperLocked(Context context) { + if (mService == null) { + Log.w(TAG, "WallpaperService not running"); + return null; + } + try { + Bundle params = new Bundle(); + ParcelFileDescriptor fd = mService.getKeyguardWallpaper(this, params); + if (fd != null) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + Bitmap bm = BitmapFactory.decodeFileDescriptor( + fd.getFileDescriptor(), null, options); + return bm; + } catch (OutOfMemoryError e) { + Log.w(TAG, "Can't decode file", e); + } finally { + try { + fd.close(); + } catch (IOException e) { + // Ignore + } + } + } + } catch (RemoteException e) { + // Ignore + } + return null; + } + private Bitmap getDefaultWallpaperLocked(Context context) { InputStream is = openDefaultWallpaper(context); if (is != null) { @@ -340,6 +401,18 @@ private Bitmap getDefaultWallpaperLocked(Context context) { } return null; } + + /** @hide */ + public void clearKeyguardWallpaper() { + synchronized (this) { + try { + mService.clearKeyguardWallpaper(); + } catch (RemoteException e) { + // ignore + } + mKeyguardWallpaper = null; + } + } } private static final Object sSync = new Object[0]; @@ -599,6 +672,15 @@ public Drawable getFastDrawable() { return null; } + /** @hide */ + public Drawable getFastKeyguardDrawable() { + Bitmap bm = sGlobals.peekKeyguardWallpaperBitmap(mContext); + if (bm != null) { + return new FastBitmapDrawable(bm); + } + return null; + } + /** * Like {@link #getFastDrawable()}, but if there is no wallpaper set, * a null pointer is returned. @@ -623,6 +705,13 @@ public Bitmap getBitmap() { return sGlobals.peekWallpaperBitmap(mContext, true); } + /** + * @hide + */ + public Bitmap getKeyguardBitmap() { + return sGlobals.peekKeyguardWallpaperBitmap(mContext); + } + /** * Remove all internal references to the last loaded wallpaper. Useful * for apps that want to reduce memory usage when they only temporarily @@ -635,6 +724,13 @@ public void forgetLoadedWallpaper() { } } + /** + * @hide + */ + public void forgetLoadedKeyguardWallpaper() { + sGlobals.forgetLoadedKeyguardWallpaper(); + } + /** * If the current wallpaper is a live wallpaper component, return the * information about that wallpaper. Otherwise, if it is a static image, @@ -786,6 +882,36 @@ public void setBitmap(Bitmap bitmap) throws IOException { } } + /** + * @param bitmap + * @throws IOException + * @hide + */ + public void setKeyguardBitmap(Bitmap bitmap) throws IOException { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return; + } + try { + ParcelFileDescriptor fd = sGlobals.mService.setKeyguardWallpaper(null, + mContext.getOpPackageName()); + if (fd == null) { + return; + } + FileOutputStream fos = null; + try { + fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); + bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos); + } finally { + if (fos != null) { + fos.close(); + } + } + } catch (RemoteException e) { + // Ignore + } + } + /** * Change the current system wallpaper to a specific byte stream. The * give InputStream is copied into persistent storage and will now be @@ -826,6 +952,34 @@ public void setStream(InputStream data) throws IOException { } } + /** + * @hide + */ + public void setKeyguardStream(InputStream data) throws IOException { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return; + } + try { + ParcelFileDescriptor fd = sGlobals.mService.setKeyguardWallpaper(null, + mContext.getOpPackageName()); + if (fd == null) { + return; + } + FileOutputStream fos = null; + try { + fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); + setWallpaper(data, fos); + } finally { + if (fos != null) { + fos.close(); + } + } + } catch (RemoteException e) { + // Ignore + } + } + private void setWallpaper(InputStream data, FileOutputStream fos) throws IOException { byte[] buffer = new byte[32768]; @@ -1088,7 +1242,29 @@ public void setWallpaperOffsetSteps(float xStep, float yStep) { mWallpaperXStep = xStep; mWallpaperYStep = yStep; } - + + /** @hide */ + public int getLastWallpaperX() { + try { + return WindowManagerGlobal.getWindowSession().getLastWallpaperX(); + } catch (RemoteException e) { + // Ignore. + } + + return -1; + } + + /** @hide */ + public int getLastWallpaperY() { + try { + return WindowManagerGlobal.getWindowSession().getLastWallpaperY(); + } catch (RemoteException e) { + // Ignore. + } + + return -1; + } + /** * Send an arbitrary command to the current active wallpaper. * @@ -1162,7 +1338,25 @@ public void clearWallpaperOffsets(IBinder windowToken) { * wallpaper. */ public void clear() throws IOException { - setStream(openDefaultWallpaper(mContext)); + clear(true); + } + + /** @hide */ + public void clear(boolean setToDefault) throws IOException { + if (setToDefault) { + setStream(openDefaultWallpaper(mContext)); + } else { + Bitmap blackBmp = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); + blackBmp.setPixel(0, 0, mContext.getResources().getColor(android.R.color.black)); + setBitmap(blackBmp); + } + } + + /** + * @hide + */ + public void clearKeyguardWallpaper() { + sGlobals.clearKeyguardWallpaper(); } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e3414d9163376..5b9d9d5ab03b4 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4461,4 +4461,24 @@ public int getPermissionGrantState(@NonNull ComponentName admin, String packageN return PERMISSION_GRANT_STATE_DEFAULT; } } + + /** + * CM: check if secure keyguard is required + * @hide + */ + public boolean requireSecureKeyguard() { + return requireSecureKeyguard(UserHandle.myUserId()); + } + + /** @hide */ + public boolean requireSecureKeyguard(int userHandle) { + if (mService != null) { + try { + return mService.requireSecureKeyguard(userHandle); + } catch (RemoteException e) { + Log.w(TAG, "Failed to get secure keyguard requirement"); + } + } + return true; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 376a3d82f7f13..a40507b68860e 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -234,4 +234,6 @@ interface IDevicePolicyManager { boolean setPermissionGrantState(in ComponentName admin, String packageName, String permission, int grantState); int getPermissionGrantState(in ComponentName admin, String packageName, String permission); + + boolean requireSecureKeyguard(int userHandle); } diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 7718a36154646..0335e2863a7bb 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -21,6 +21,8 @@ import android.content.res.XmlResourceParser; import android.os.*; import android.os.Process; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; @@ -207,6 +209,8 @@ public static class BackupScheme { final int mFullBackupContent; final PackageManager mPackageManager; + final StorageManager mStorageManager; + final StorageVolume[] mVolumes; final String mPackageName; /** @@ -230,6 +234,15 @@ String tokenToDirectoryPath(String domainToken) { } else { return null; } + } else if (domainToken.startsWith(FullBackup.SHARED_PREFIX)) { + int slash = domainToken.indexOf('/'); + int i = Integer.parseInt(domainToken.substring(slash + 1)); + + if (i < mVolumes.length) { + return mVolumes[i].getPath(); + } else { + Log.e(TAG, "Could not find volume for " + domainToken); + } } else if (domainToken.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) { return NOBACKUP_DIR.getCanonicalPath(); } @@ -263,6 +276,8 @@ String tokenToDirectoryPath(String domainToken) { SHAREDPREF_DIR = context.getSharedPrefsFile("foo").getParentFile(); CACHE_DIR = context.getCacheDir(); NOBACKUP_DIR = context.getNoBackupFilesDir(); + mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + mVolumes = mStorageManager.getVolumeList(); if (android.os.Process.myUid() != Process.SYSTEM_UID) { EXTERNAL_DIR = context.getExternalFilesDir(null); } else { diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index 0fce4e2cd1841..a88aa312550dd 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -165,14 +165,18 @@ public void add(UsageStats right) { mPackageName + "' with UsageStats for package '" + right.mPackageName + "'."); } - if (right.mEndTimeStamp > mEndTimeStamp) { + if (right.mBeginTimeStamp > mBeginTimeStamp) { + // The incoming UsageStat begins after this one, so use its last time used fields + // as the source of truth. + // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with + // regards to their mEndTimeStamp. mLastEvent = right.mLastEvent; - mEndTimeStamp = right.mEndTimeStamp; mLastTimeUsed = right.mLastTimeUsed; mBeginIdleTime = right.mBeginIdleTime; mLastTimeSystemUsed = right.mLastTimeSystemUsed; } mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); + mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp); mTotalTimeInForeground += right.mTotalTimeInForeground; mLaunchCount += right.mLaunchCount; } diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java old mode 100644 new mode 100755 index 2e273459add89..74302f27ec1d9 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -370,6 +370,89 @@ && isValidDevice(device)) { return null; } + /** + * Set priority of the profile + * + *

The device should already be paired. + * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager + * {@link #PRIORITY_OFF}, + * + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error + * @hide + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON){ + return false; + } + try { + return mService.setPriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get the priority of the profile. + * + *

The priority can be any of: + * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, + * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + *

Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device + * @hide + */ + public int getPriority(BluetoothDevice device) { + if (VDBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getPriority(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } + + /** + * Check if A2DP profile is streaming music. + * + *

Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device BluetoothDevice device + */ + public boolean isA2dpPlaying(BluetoothDevice device) { + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.isA2dpPlaying(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + /** * Helper for converting a state to a string. * diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 1f3ff51487168..71183d9f561df 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1,6 +1,8 @@ /* * Copyright (C) 2009-2015 The Android Open Source Project * Copyright (C) 2015 Samsung LSI + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +25,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -92,7 +95,7 @@ */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; - private static final boolean DBG = true; + private static final boolean DBG = false; private static final boolean VDBG = false; /** @@ -538,6 +541,7 @@ public static synchronized BluetoothAdapter getDefaultAdapter() { * @throws IllegalArgumentException if address is invalid */ public BluetoothDevice getRemoteDevice(String address) { + android.util.SeempLog.record(62); return new BluetoothDevice(address); } @@ -553,6 +557,7 @@ public BluetoothDevice getRemoteDevice(String address) { * @throws IllegalArgumentException if address is invalid */ public BluetoothDevice getRemoteDevice(byte[] address) { + android.util.SeempLog.record(62); if (address == null || address.length != 6) { throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); } @@ -762,7 +767,7 @@ public boolean enableBLE() { try { if (DBG) Log.d(TAG, "Calling enableBLE"); mManagerService.updateBleAppCount(mToken, true); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -784,6 +789,7 @@ public boolean enableBLE() { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { + android.util.SeempLog.record(63); try { synchronized(mManagerCallback) { if (mService != null) @@ -880,6 +886,7 @@ else if (getLeState() == STATE_BLE_ON) */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { + android.util.SeempLog.record(56); int state = STATE_OFF; if (isEnabled() == true){ if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); @@ -898,7 +905,7 @@ public boolean enable() { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -929,6 +936,7 @@ public boolean enable() { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { + android.util.SeempLog.record(57); try { return mManagerService.disable(true); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -946,6 +954,7 @@ public boolean disable() { * @hide */ public boolean disable(boolean persist) { + android.util.SeempLog.record(57); try { return mManagerService.disable(persist); @@ -1190,6 +1199,7 @@ public void setDiscoverableTimeout(int timeout) { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startDiscovery() { + android.util.SeempLog.record(58); if (getState() != STATE_ON) return false; try { synchronized(mManagerCallback) { @@ -1408,6 +1418,7 @@ record = mService.reportActivityInfo(); */ @RequiresPermission(Manifest.permission.BLUETOOTH) public Set getBondedDevices() { + android.util.SeempLog.record(61); if (getState() != STATE_ON) { return toDeviceSet(new BluetoothDevice[0]); } @@ -1460,6 +1471,7 @@ public int getConnectionState() { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getProfileConnectionState(int profile) { + android.util.SeempLog.record(64); if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { synchronized(mManagerCallback) { @@ -1582,6 +1594,7 @@ public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUI @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) throws IOException { + android.util.SeempLog.record(59); return createNewRfcommSocketAndRecord(name, uuid, false, false); } @@ -1754,6 +1767,117 @@ public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException { return listenUsingL2capOn(port, false, false); } + + /** + * Construct an insecure L2CAP server socket. + * Call #accept to retrieve connections to this socket. + *

To auto assign a port without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * @param port the PSM to listen on + * @return An L2CAP BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_L2CAP, false, false, port, false, false); + int errno = socket.mSocket.bindListen(); + if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + socket.setChannel(socket.mSocket.getPort()); + } + if (errno != 0) { + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); + } + return socket; + + } + + /** + * Create a client side Message Access Profile Service Record. + * Create the record once, and reuse it for all connections. + * If changes to a record is needed remove the old record using {@link removeSdpRecord} + * and then create a new one. + * WARNING: This API requires removeSdpRecord() to be called, to avoid leaking resources! + * A second call to this function - either from two different apps or from the + * same app, without first calling removeSdpRecord() - will make the device + * break the Bluetooth spec, which could lead to severe IOP issues. + * @param serviceName The textual name of the service + * @param rfcommChannel The RFCOMM channel that clients can connect to + * (obtain from BluetoothServerSocket) + * @param l2capPsm The L2CAP PSM channel that clients can connect to + * (obtain from BluetoothServerSocket) + * Supply -1 to omit the L2CAP PSM from the record. + * @param version The Profile version number (As specified in the Bluetooth + * MAP specification) + * @param features The feature bit mask (As specified in the Bluetooth + * MAP specification) + * @return a handle to the record created. The record can be removed again + * using {@link removeSdpRecord}(). The record is not linked to the + * creation/destruction of BluetoothSockets, hence SDP record cleanup + * is a separate process. + * returns -1 if an error occure and the record was not created. + * @hide + */ + public int createMapMnsSdpRecord(String serviceName, int rfcommChannel, + int l2capPsm, int version, int features) { + try { + return mService.createMapMnsSdpRecord(serviceName, rfcommChannel, + l2capPsm, version, features); + } catch (RemoteException e) { + Log.e(TAG, "createMapMnsSdpRecord: ", e); + } + return -1; + } + + /** + * Create a client side Phonebook Access Profile Service Record. + * Create the record once, and reuse it for all connections. + * If changes to a record is needed remove the old record using {@link removeSdpRecord} + * and then create a new one. + * WARNING: This API requires removeSdpRecord() to be called, to avoid leaking resources! + * A second call to this function - either from two different apps or from the + * same app, without first calling removeSdpRecord() - will make the device + * break the Bluetooth spec, which could lead to severe IOP issues. + * @param serviceName The textual name of the service + * @param version The Profile version number (As specified in the Bluetooth + * PBAP specification) + * @return a handle to the record created. The record can be removed again + * using {@link removeSdpRecord}(). The record is not linked to the + * creation/destruction of BluetoothSockets, hence SDP record cleanup + * is a separate process. + * returns -1 if an error occure and the record was not created. + * @hide + */ + public int createPbapPceSdpRecord(String serviceName, int version) { + try { + return mService.createPbapPceSdpRecord(serviceName, version); + } catch (RemoteException e) { + Log.e(TAG, "createPbapPceSdpRecord: ", e); + } + return -1; + } + + /** + * Remove a SDP record created using createSdpRecord(). + * This function shall be called before a new call to createSdpRecord for the same record + * type can be made, unless the record type created supports multiple instances. + * @param recordHandle handle of the record to remove - provided by createSdpRecord() + * @return true if success + * @hide + */ + public boolean removeSdpRecord(int recordHandle){ + try { + return mService.removeSdpRecord(recordHandle); + } catch (RemoteException e) { + Log.e(TAG, "removeSdpRecord: ", e); + } + return false; + } + /** * Read the local Out of Band Pairing Data *

Requires {@link android.Manifest.permission#BLUETOOTH} @@ -1825,6 +1949,9 @@ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); return true; + } else if (profile == BluetoothProfile.DUN) { + BluetoothDun dun = new BluetoothDun(context, listener); + return true; } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; @@ -1837,6 +1964,9 @@ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener } else if (profile == BluetoothProfile.SAP) { BluetoothSap sap = new BluetoothSap(context, listener); return true; + } else if (profile == BluetoothProfile.HID_DEVICE) { + BluetoothHidDevice hidd = new BluetoothHidDevice(context, listener); + return true; } else { return false; } @@ -1881,6 +2011,10 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { BluetoothPan pan = (BluetoothPan)proxy; pan.close(); break; + case BluetoothProfile.DUN: + BluetoothDun dun = (BluetoothDun)proxy; + dun.close(); + break; case BluetoothProfile.HEALTH: BluetoothHealth health = (BluetoothHealth)proxy; health.close(); @@ -1905,6 +2039,10 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { BluetoothSap sap = (BluetoothSap)proxy; sap.close(); break; + case BluetoothProfile.HID_DEVICE: + BluetoothHidDevice hidd = (BluetoothHidDevice) proxy; + hidd.close(); + break; } } @@ -1929,13 +2067,14 @@ public void onBluetoothServiceUp(IBluetooth bluetoothService) { } public void onBluetoothServiceDown() { - if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); + Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; if (mLeScanClients != null) mLeScanClients.clear(); if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); synchronized (mProxyServiceStateCallbacks) { + Log.d(TAG, "onBluetoothServiceDown: Sending callbacks to " + mProxyServiceStateCallbacks.size() + " clients"); for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { @@ -1947,6 +2086,7 @@ public void onBluetoothServiceDown() { } } } + Log.d(TAG, "onBluetoothServiceDown: Finished sending callbacks to registered clients"); } public void onBrEdrDown() { diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java index b53a8fc0f6fc7..48510876d04c5 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -210,7 +211,7 @@ && isValidDevice(device)) { } public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { - if (DBG) Log.d(TAG, "sendPassThroughCmd"); + if (DBG) Log.d(TAG, "sendPassThroughCmd dev = " + device + " key " + keyCode + " State = " + keyState); if (mService != null && isEnabled()) { try { mService.sendPassThroughCmd(device, keyCode, keyState); @@ -223,6 +224,90 @@ public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState if (mService == null) Log.w(TAG, "Proxy not attached to service"); } + public void getMetaData(int[] attributeIds) { + if (DBG) Log.d(TAG, "getMetaData num requested Ids = " + attributeIds.length); + if (mService != null && isEnabled()) { + try { + mService.getMetaData(attributeIds); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getMetaData", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + public void getPlayStatus(int[] playStatusIds) { + if (DBG) Log.d(TAG, "getPlayStatus num requested Ids = "+ playStatusIds.length); + if (mService != null && isEnabled()) { + try { + mService.getPlayStatus(playStatusIds); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getPlayStatus()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + public void getPlayerApplicationSetting() { + if (DBG) Log.d(TAG, "getPlayerApplicationSetting"); + if (mService != null && isEnabled()) { + try { + mService.getPlayerApplicationSetting(); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getPlayerApplicationSetting()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + public void setPlayerApplicationSetting(int attributeId, int attributeVal) { + if (DBG) Log.d(TAG, "setPlayerApplicationSetting attribId = " + attributeId + " attribVal = " + attributeVal); + if (mService != null && isEnabled()) { + try { + mService.setPlayerApplicationSetting(attributeId, attributeVal); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + public BluetoothAvrcpInfo getSupportedPlayerAppSetting(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getSupportedPlayerAppSetting dev = " + device); + if (mService != null && isEnabled()) { + try { + return mService.getSupportedPlayerAppSetting(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getSupportedPlayerAppSetting()", e); + return null; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } + + public int getSupportedFeatures(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getSupportedFeatures dev = " + device); + if (mService != null && isEnabled()) { + try { + return mService.getSupportedFeatures(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getSupportedFeatures()", e); + return 0; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return 0; + } + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/core/java/android/bluetooth/BluetoothAvrcpInfo.aidl b/core/java/android/bluetooth/BluetoothAvrcpInfo.aidl new file mode 100644 index 0000000000000..9b85c80717dc7 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothAvrcpInfo.aidl @@ -0,0 +1,34 @@ +/* +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package android.bluetooth; + +parcelable BluetoothAvrcpInfo; diff --git a/core/java/android/bluetooth/BluetoothAvrcpInfo.java b/core/java/android/bluetooth/BluetoothAvrcpInfo.java new file mode 100644 index 0000000000000..a815d101830f9 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothAvrcpInfo.java @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package android.bluetooth; + +import java.util.ArrayList; +import android.util.Log; + +import android.os.Parcel; +import android.os.Parcelable; +import android.provider.BaseColumns; +import android.net.Uri; + +/** + * Represents the AVRCP Metadata of remote Bluetooth Device. + * + * {@see BluetoothAvrcpController} + * + * {@hide} + */ +public final class BluetoothAvrcpInfo implements Parcelable, BaseColumns{ + + private byte[] supportedPlayerAttributes;// attributes supported + private byte[] numSupportedPlayerAttribValues; // number of values of each attribute + private String TAG = "BluetoothAvrcpInfo"; + /* + * This would a list of values of all AttributeIds + */ + private byte[] supportedPlayerAtribValues; // actual values lies here. + + /* Default Constructor */ + public BluetoothAvrcpInfo() { + supportedPlayerAttributes = null; + numSupportedPlayerAttribValues = null; + supportedPlayerAtribValues = null; + } + public BluetoothAvrcpInfo(byte[] attribIds, byte[] numValueSupported, byte[] valuesSupported) { + int numAttributes = attribIds.length; + int zz = 0; + supportedPlayerAttributes = new byte[numAttributes]; + numSupportedPlayerAttribValues = new byte[numAttributes]; + supportedPlayerAtribValues = new byte[valuesSupported.length]; + for (zz = 0; zz < numAttributes; zz++) { + supportedPlayerAttributes[zz] = attribIds[zz]; + numSupportedPlayerAttribValues[zz] = numValueSupported[zz]; + } + for (zz = 0; zz < supportedPlayerAtribValues.length; zz++) + supportedPlayerAtribValues[zz] = valuesSupported[zz]; + } + /* + * Reading Structure back from Paracel + */ + public BluetoothAvrcpInfo(Parcel source){ + ArrayList attribs = new ArrayList(); + ArrayList numAttribVal = new ArrayList(); + ArrayList attribVals = new ArrayList(); + Byte numAttributes = source.readByte(); + /* + * Read from Source + */ + for(int xx = 0; xx < numAttributes ; xx++) { + attribs.add(source.readByte()); + numAttribVal.add(source.readByte()); + for (int zz = 0; zz < numAttribVal.get(xx); zz++) { + attribVals.add(source.readByte()); + } + } + + /* + * Write Back to Private Data Structures + */ + supportedPlayerAttributes = new byte[attribs.size()]; + for (int zz = 0; zz< attribs.size(); zz++) { + supportedPlayerAttributes[zz] = attribs.get(zz); + } + + numSupportedPlayerAttribValues = new byte[numAttribVal.size()]; + for (int zz = 0; zz< numAttribVal.size(); zz++) { + numSupportedPlayerAttribValues[zz] = numAttribVal.get(zz); + } + + supportedPlayerAtribValues = new byte[attribVals.size()]; + for (int zz = 0; zz< attribVals.size(); zz++) { + supportedPlayerAtribValues[zz] = attribVals.get(zz); + } + } + + public int describeContents() { + return 0; + } + + /* While flatenning the structure we would use the follwing way + * NumAttributes,ID, numValues, Values + */ + public void writeToParcel(Parcel out, int flags) { + byte numSuppAttributes = (byte)supportedPlayerAttributes.length; + out.writeByte(numSuppAttributes); + for (int xx = 0; xx < numSuppAttributes; xx++) { + out.writeByte(supportedPlayerAttributes[xx]); + out.writeByte(numSupportedPlayerAttribValues[xx]); + for (int zz = 0; zz < numSupportedPlayerAttribValues[xx]; zz++) { + out.writeByte(supportedPlayerAtribValues[zz]); + } + } + } + + public byte[] getSupportedPlayerAttributes() { + return supportedPlayerAttributes; + } + + public byte getNumSupportedPlayerAttributeVal(byte playerAttributeId) { + for (int zz = 0; zz < supportedPlayerAttributes.length; zz++) { + if (playerAttributeId == supportedPlayerAttributes[zz]) { + return numSupportedPlayerAttribValues[zz]; + } + } + return 0; + } + + public byte[] getSupportedPlayerAttributeVlaues (byte playerAttributeId) { + int index = 0; + int zz = 0; + boolean attributeFound = false; + for (zz = 0; zz < supportedPlayerAttributes.length; zz++) { + if (playerAttributeId == supportedPlayerAttributes[zz]) { + attributeFound = true; + break; + } + else + index = index + numSupportedPlayerAttribValues[zz]; + } + if (attributeFound) { + byte[] supportedValues = new byte[numSupportedPlayerAttribValues[zz]]; + for (int xx = 0; xx < numSupportedPlayerAttribValues[zz]; xx++) + supportedValues[xx] = supportedPlayerAtribValues[xx + index]; + return supportedValues; + } + else + return new byte[0]; + } + public void putPlayerSettingAttributes(byte[] attribIds, byte[] numValueSupported, byte[] valuesSupported) { + int numAttributes = attribIds.length; + int zz = 0; + supportedPlayerAttributes = new byte[numAttributes]; + numSupportedPlayerAttribValues = new byte[numAttributes]; + supportedPlayerAtribValues = new byte[valuesSupported.length]; + for (zz = 0; zz < numAttributes; zz++) { + supportedPlayerAttributes[zz] = attribIds[zz]; + numSupportedPlayerAttribValues[zz] = numValueSupported[zz]; + } + for (zz = 0; zz < supportedPlayerAtribValues.length; zz++) + supportedPlayerAtribValues[zz] = valuesSupported[zz]; + } + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public BluetoothAvrcpInfo createFromParcel(Parcel in) { + return new BluetoothAvrcpInfo(in); + } + public BluetoothAvrcpInfo[] newArray(int size) { + return new BluetoothAvrcpInfo[size]; + } + }; + + public static final String PERMISSION_ACCESS = "android.permission.ACCESS_BLUETOOTH_AVRCP_CT_DATA"; + public static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.avrcp/btavrcp_ct"); + + /* + * BaseColumns already has _ID and COUNT values + * Below mentioned strings are used to implement different columns + * of AVRCP MetaData table. + * TRACK_NUM : Ineteger value containing the order number of + * the audio-file on its original recording. + * Numeric ASCII string converted to Integer + * TITLE : Text field representing the title, song name + * ARTIST_NAME : Text field representing artist(s), performer(s) + * ALBUM_NAME : Text field representing the title of the recording + * (source) from which the audio in the file is taken. + * TOTAL_TRACKS : Integet value containing the total number of tracks + * or elements on the original recording. + * GENRE : Text field representing the category of the composition + * characterized by a particular style. + * PLAYING_TIME : Integer containing the length of the audio file in + * milliseconds for eg 02:30 = 150000 + * PLAY_STATUS : Text feild showing current state of track. Possible + * values would be Playing, Stopped, Paused, Forward_Seek + * REV_SEEK + * REPEAT_STATUS : String describing Repeat mode status on remote Media Player + * Posible values "NOT SUPPORTED", "OFF" "Single Track Repeat" + * "All Track Repeat" "Group Repeat" + * SHUFFLE_STATUS : String describing Shuffle mode status on remote Media Player + * Posible values "NOT SUPPORTED", "OFF" "All Track Shuffle" + * "Group Shuffle" + * SCAN_STAUS : String describing SCAN mode status on remote Media Player + * Possible values "NOT SUPPORTED", "OFF","ALL Tracks Scan" + * "Group Scan" + * + * EQUALIZER_STATUS: String describing EQUALIZER mode status on remote Media Player + * Possible values "NOT SUPPORTED", "OFF","ON" + */ + public static final String TRACK_NUM = "track_num"; + public static final String TITLE = "title"; + public static final String ARTIST_NAME = "artist_name"; + public static final String ALBUM_NAME = "album_name"; + public static final String TOTAL_TRACKS = "total_tracks"; + public static final String GENRE = "genre"; + public static final String PLAYING_TIME = "playing_time"; + public static final String TOTAL_TRACK_TIME = "total_track_time"; + public static final String PLAY_STATUS = "play_status"; + public static final String REPEAT_STATUS = "repeat_status"; + public static final String SHUFFLE_STATUS = "shuffle_status"; + public static final String SCAN_STATUS = "scan_status"; + public static final String EQUALIZER_STATUS = "equalizer_status"; + + /* + * Default values for each of the items + */ + public static final int TRACK_NUM_INVALID = 0xFF; + public static final String TITLE_INVALID = "NOT_SUPPORTED"; + public static final String ARTIST_NAME_INVALID = "NOT_SUPPORTED"; + public static final String ALBUM_NAME_INVALID = "NOT_SUPPORTED"; + public static final int TOTAL_TRACKS_INVALID = 0xFF; + public static final String GENRE_INVALID = "NOT_SUPPORTED"; + public static final int PLAYING_TIME_INVALID = 0xFF; + public static final int TOTAL_TRACK_TIME_INVALID = 0xFF; + public static final String PLAY_STATUS_INVALID = "NOT_SUPPORTED"; + public static final String REPEAT_STATUS_INVALID = "NOT_SUPPORTED"; + public static final String SHUFFLE_STATUS_INVALID = "NOT_SUPPORTED"; + public static final String SCAN_STATUS_INVALID = "NOT_SUPPORTED"; + public static final String EQUALIZER_STATUS_INVALID = "NOT_SUPPORTED"; + + /* + *Element Id Values for GetMetaData + */ + public static final int MEDIA_ATTRIBUTE_ALL = 0x00; + public static final int MEDIA_ATTRIBUTE_TITLE = 0x01; + public static final int MEDIA_ATTRIBUTE_ARTIST_NAME = 0x02; + public static final int MEDIA_ATTRIBUTE_ALBUM_NAME = 0x03; + public static final int MEDIA_ATTRIBUTE_TRACK_NUMBER = 0x04; + public static final int MEDIA_ATTRIBUTE_TOTAL_TRACK_NUMBER = 0x05; + public static final int MEDIA_ATTRIBUTE_GENRE = 0x06; + public static final int MEDIA_ATTRIBUTE_PLAYING_TIME = 0x07; + + /* + *PlayStatusId Values for GetPlayStatus + */ + public static final int MEDIA_PLAYSTATUS_ALL = 0x08; + public static final int MEDIA_PLAYSTATUS_SONG_TOTAL_LEN = 0x09; + public static final int MEDIA_PLAYSTATUS_SONG_CUR_POS = 0x0a; + public static final int MEDIA_PLAYSTATUS_SONG_PLAY_STATUS = 0x0b; + + /* + * Values for SetPlayerApplicationSettings + */ + public static final byte ATTRIB_EQUALIZER_STATUS = 0x01; + public static final byte ATTRIB_REPEAT_STATUS = 0x02; + public static final byte ATTRIB_SHUFFLE_STATUS = 0x03; + public static final byte ATTRIB_SCAN_STATUS = 0x04; + + public static final byte EQUALIZER_STATUS_OFF = 0x01; + public static final byte EQUALIZER_STATUS_ON = 0x02; + + public static final byte REPEAT_STATUS_OFF = 0x01; + public static final byte REPEAT_STATUS_SINGLE_TRACK_REPEAT = 0x02; + public static final byte REPEAT_STATUS_ALL_TRACK_REPEAT = 0x03; + public static final byte REPEAT_STATUS_GROUP_REPEAT = 0x04; + + public static final byte SHUFFLE_STATUS_OFF = 0x01; + public static final byte SHUFFLE_STATUS_ALL_TRACK_SHUFFLE = 0x02; + public static final byte SHUFFLE_STATUS_GROUP_SHUFFLE = 0x03; + + public static final byte SCAN_STATUS_OFF = 0x01; + public static final byte SCAN_STATUS_ALL_TRACK_SCAN = 0x02; + public static final byte SCAN_STATUS_GROUP_SCAN = 0x03; + + public static final int BTRC_FEAT_METADATA = 0x01; + public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02; + public static final int BTRC_FEAT_BROWSE = 0x04; + +} diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java old mode 100644 new mode 100755 index 54bf4afa93ffb..4a38287e7ca9c --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -283,6 +283,8 @@ public int getDeviceClass() { public static final int PROFILE_PANU = 4; /** @hide */ public static final int PROFILE_NAP = 5; + /** @hide */ + public static final int PROFILE_A2DP_SINK = 6; /** * Check class bits for possible bluetooth profile support. @@ -310,6 +312,21 @@ public boolean doesClassMatch(int profile) { default: return false; } + } else if (profile == PROFILE_A2DP_SINK) { + if (hasService(Service.CAPTURE)) { + return true; + } + // By the A2DP spec, srcs must indicate the CAPTURE service. + // However if some device that do not, we try to + // match on some other class bits. + switch (getDeviceClass()) { + case Device.AUDIO_VIDEO_HIFI_AUDIO: + case Device.AUDIO_VIDEO_SET_TOP_BOX: + case Device.AUDIO_VIDEO_VCR : + return true; + default: + return false; + } } else if (profile == PROFILE_HEADSET) { // The render service class is required by the spec for HFP, so is a // pretty good signal diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index cd5c205d14dc3..d7d248f650f90 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1166,12 +1166,12 @@ public boolean setPasskey(int passkey) { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + *

Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true confirmation has been sent out * false for error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); @@ -1402,6 +1402,27 @@ public BluetoothSocket createL2capSocket(int channel) throws IOException { null); } + /** + * Create an L2cap {@link BluetoothSocket} ready to start an insecure + * outgoing connection to this remote device on given channel. + *

The remote device will be not authenticated and communication on this + * socket will not be encrypted. + *

Use {@link BluetoothSocket#connect} to initiate the outgoing + * connection. + *

Valid L2CAP PSM channels are in range 1 to 2^16. + *

Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param channel L2cap PSM/channel to connect to + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + * @hide + */ + public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel, + null); + } + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device using SDP lookup of uuid. diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java index c794be2e2c9ef..51d14cc0a117a 100644 --- a/core/java/android/bluetooth/BluetoothDevicePicker.java +++ b/core/java/android/bluetooth/BluetoothDevicePicker.java @@ -43,6 +43,14 @@ public interface BluetoothDevicePicker { public static final String ACTION_DEVICE_SELECTED = "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; + /** + * Broadcast when no BT device is selected from BT device picker screen. + * This happens when user presses back button. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEVICE_NOT_SELECTED = + "org.codeaurora.bluetooth.devicepicker.action.DEVICE_NOT_SELECTED"; + /** * Broadcast when someone want to select one BT device from devices list. * This intent contains below extra data: diff --git a/core/java/android/bluetooth/BluetoothDun.java b/core/java/android/bluetooth/BluetoothDun.java new file mode 100644 index 0000000000000..09120610caa3f --- /dev/null +++ b/core/java/android/bluetooth/BluetoothDun.java @@ -0,0 +1,296 @@ +/* +*Copyright (c) 2013, The Linux Foundation. All rights reserved. +* +*Redistribution and use in source and binary forms, with or without +*modification, are permitted provided that the following conditions are +*met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of The Linux Foundation nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +*THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +*WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +*MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +*ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +*BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +*CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +*SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +*BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +*WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +*OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +*IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +package android.bluetooth; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the APIs to control the Bluetooth Dun + * Profile. + * + *

BluetoothDun is a proxy object for controlling the Bluetooth DUN + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothDun proxy object. + * + *

Each method is protected with its appropriate permission. + *@hide + */ +public final class BluetoothDun implements BluetoothProfile { + private static final String TAG = "BluetoothDun"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + /** + * Intent used to broadcast the change in connection state of the Dun + * profile. + * + *

This intent will have 3 extras: + *

    + *
  • {@link #EXTRA_STATE} - The current state of the profile.
  • + *
  • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
  • + *
  • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
  • + *
+ * + *

{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTED}. + * + *

Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "codeaurora.bluetooth.dun.profile.action.CONNECTION_STATE_CHANGED"; + + private Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetoothDun mDunService; + + /** + * Create a BluetoothDun proxy object for interacting with the local + * Bluetooth Service which handles the Dun profile + * + */ + /*package*/ BluetoothDun(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + try { + mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); + } + Log.d(TAG, "BluetoothDun() call bindService"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothDun.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { + Log.e(TAG, "Could not bind to Bluetooth Dun Service with " + intent); + return false; + } + return true; + } + + + /*package*/ void close() { + if (VDBG) log("close()"); + mServiceListener = null; + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re); + } + } + + synchronized (mConnection) { + if ( mDunService != null) { + try { + mDunService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + + protected void finalize() { + close(); + } + + private IBluetoothStateChangeCallback mStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + + @Override + public void onBluetoothStateChange(boolean on) { + //Handle enable request to bind again. + Log.d(TAG, "onBluetoothStateChange on: " + on); + if (on) { + try { + if (mDunService == null) { + Log.d(TAG, "onBluetoothStateChange call bindService"); + doBind(); + } + } catch (IllegalStateException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to DUN service: ", e); + } catch (SecurityException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to DUN service: ", e); + } + } else { + if (VDBG) Log.d(TAG,"Unbinding service..."); + synchronized (mConnection) { + if ( mDunService != null) { + try { + mDunService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + } + }; + + /** + * Initiate disconnection from DUN server. + * + *

Once the disconnection is initiated by any device either local host + * or remote device, the state will transition from {@link #STATE_CONNECTED} + * to {@link #STATE_DISCONNECTED}. + * + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mDunService != null && isEnabled() && + isValidDevice(device)) { + try { + return mDunService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + /** + * {@inheritDoc} + */ + public List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + if (mDunService != null && isEnabled()) { + try { + return mDunService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + if (mDunService != null && isEnabled()) { + try { + return mDunService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList(); + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + if (mDunService != null && isEnabled() + && isValidDevice(device)) { + try { + return mDunService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "BluetoothDUN Proxy object connected"); + mDunService = IBluetoothDun.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.DUN, + BluetoothDun.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "BluetoothDUN Proxy object disconnected"); + mDunService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.DUN); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 09a15de8778e5..da810329aa60e 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -20,11 +20,11 @@ import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; -import android.os.Handler; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.IBinder; -import android.os.Looper; -import android.os.Message; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -47,7 +47,7 @@ public final class BluetoothHeadset implements BluetoothProfile { private static final String TAG = "BluetoothHeadset"; private static final boolean DBG = true; - private static final boolean VDBG = false; + private static final boolean VDBG = true; /** * Intent used to broadcast the change in connection state of the Headset @@ -128,6 +128,13 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT"; + /** + * @hide Broadcast intent when HF indicator value changed is updated by HS. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_HF_INDICATOR_VALUE_CHANGED = + "codeaurora.bluetooth.headset.action.ACTION_HF_INDICATOR_VALUE_CHANGED"; + /** * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} * intents that contains the name of the vendor-specific command. @@ -198,6 +205,20 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID"; + /** + * @hide Used for sharing the HF indicator assigned number. + */ + public static final String HF_INDICATOR_ASSIGNED_NUMBER = + "codeaurora.bluetooth.headset.intent.category.anum"; + + + /** + * @hide Used for sharing the HF indicator assigned number's value. + */ + public static final String HF_INDICATOR_ASSIGNED_NUMBER_VALUE = + "codeaurora.bluetooth.headset.intent.category.anumvalue"; + + /** * Headset state when SCO audio is not connected. * This state can be one of @@ -222,8 +243,6 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final int STATE_AUDIO_CONNECTED = 12; - private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; - private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101; private Context mContext; private ServiceListener mServiceListener; @@ -236,7 +255,14 @@ public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { if (VDBG) Log.d(TAG,"Unbinding service..."); - doUnbind(); + synchronized (mConnection) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } } else { synchronized (mConnection) { try { @@ -273,26 +299,15 @@ public void onBluetoothStateChange(boolean up) { } boolean doBind() { - try { - return mAdapter.getBluetoothManager().bindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind HeadsetService", e); - } - return false; - } - - void doUnbind() { - synchronized (mConnection) { - if (mService != null) { - try { - mAdapter.getBluetoothManager().unbindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG,"Unable to unbind HeadsetService", e); - } - } + Intent intent = new Intent(IBluetoothHeadset.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + Log.e(TAG, "Could not bind to Bluetooth Headset Service with " + intent); + return false; } + return true; } /** @@ -312,8 +327,18 @@ void doUnbind() { Log.e(TAG,"",e); } } + + synchronized (mConnection) { + if (mService != null) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } mServiceListener = null; - doUnbind(); } /** @@ -969,21 +994,21 @@ public boolean disableWBS() { return false; } - private final IBluetoothProfileServiceConnection mConnection - = new IBluetoothProfileServiceConnection.Stub() { - @Override + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothHeadset.Stub.asInterface(service); - mHandler.sendMessage(mHandler.obtainMessage( - MESSAGE_HEADSET_SERVICE_CONNECTED)); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this); + } } - @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; - mHandler.sendMessage(mHandler.obtainMessage( - MESSAGE_HEADSET_SERVICE_DISCONNECTED)); + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); + } } }; @@ -1007,25 +1032,4 @@ private boolean isValidDevice(BluetoothDevice device) { private static void log(String msg) { Log.d(TAG, msg); } - - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_HEADSET_SERVICE_CONNECTED: { - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, - BluetoothHeadset.this); - } - break; - } - case MESSAGE_HEADSET_SERVICE_DISCONNECTED: { - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); - } - break; - } - } - } - }; } diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 874026fb157d8..484a856539b86 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -100,7 +100,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * {@link #EXTRA_BATTERY_LEVEL}, * {@link #EXTRA_OPERATOR_NAME}, * {@link #EXTRA_VOICE_RECOGNITION}, - * {@link #EXTRA_IN_BAND_RING}

+ * {@link #EXTRA_IN_BAND_RING} + * {@link #EXTRA_MANF_ID} + * {@link #EXTRA_MANF_MODEL}

*/ public static final String ACTION_AG_EVENT = "android.bluetooth.headsetclient.profile.action.AG_EVENT"; @@ -205,6 +207,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final String EXTRA_SUBSCRIBER_INFO = "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO"; + /** + * Extra for AG_EVENT intent indicates manufacturer identification. + *

Value: String containing manufacturer identification.

+ */ + public static final String EXTRA_MANF_ID = + "android.bluetooth.headsetclient.extra.MANF_ID"; + + /** + * Extra for AG_EVENT intent indicates manufacturer model. + *

Value: String containing manufacturer model.

+ */ + public static final String EXTRA_MANF_MODEL = + "android.bluetooth.headsetclient.extra.MANF_MODEL"; + + /** * Extra for AG_CALL_CHANGED intent indicates the * {@link BluetoothHeadsetClientCall} object that has changed. diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java new file mode 100644 index 0000000000000..468df4db8b9db --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Arrays; +import java.util.List; + +/** + * @hide + */ +public final class BluetoothHidDevice implements BluetoothProfile { + + private static final String TAG = BluetoothHidDevice.class.getSimpleName(); + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "codeaurora.bluetooth.hid.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Constants representing device subclass. + * + * @see #registerApp(String, String, String, byte, byte[], + * BluetoothHidDeviceCallback) + */ + public static final byte SUBCLASS1_NONE = (byte) 0x00; + public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; + public static final byte SUBCLASS1_MOUSE = (byte) 0x80; + public static final byte SUBCLASS1_COMBO = (byte) 0xC0; + + public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00; + public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01; + public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; + public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; + public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; + public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; + public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; + + /** + * Constants representing report types. + * + * @see BluetoothHidDeviceCallback#onGetReport(byte, byte, int) + * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[]) + * @see BluetoothHidDeviceCallback#onIntrData(byte, byte[]) + */ + public static final byte REPORT_TYPE_INPUT = (byte) 1; + public static final byte REPORT_TYPE_OUTPUT = (byte) 2; + public static final byte REPORT_TYPE_FEATURE = (byte) 3; + + /** + * Constants representing error response for Set Report. + * + * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[]) + */ + public static final byte ERROR_RSP_SUCCESS = (byte) 0; + public static final byte ERROR_RSP_NOT_READY = (byte) 1; + public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2; + public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3; + public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4; + public static final byte ERROR_RSP_UNKNOWN = (byte) 14; + + /** + * Constants representing protocol mode used set by host. Default is always + * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise. + * + * @see BluetoothHidDeviceCallback#onSetProtocol(byte) + */ + public static final byte PROTOCOL_BOOT_MODE = (byte) 0; + public static final byte PROTOCOL_REPORT_MODE = (byte) 1; + + private Context mContext; + + private ServiceListener mServiceListener; + + private IBluetoothHidDevice mService; + + private BluetoothAdapter mAdapter; + + private static class BluetoothHidDeviceCallbackWrapper extends IBluetoothHidDeviceCallback.Stub { + + private BluetoothHidDeviceCallback mCallback; + + public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) { + mCallback = callback; + } + + @Override + public void onAppStatusChanged(BluetoothDevice pluggedDevice, + BluetoothHidDeviceAppConfiguration config, boolean registered) { + mCallback.onAppStatusChanged(pluggedDevice, config, registered); + } + + @Override + public void onConnectionStateChanged(BluetoothDevice device, int state) { + mCallback.onConnectionStateChanged(device, state); + } + + @Override + public void onGetReport(byte type, byte id, int bufferSize) { + mCallback.onGetReport(type, id, bufferSize); + } + + @Override + public void onSetReport(byte type, byte id, byte[] data) { + mCallback.onSetReport(type, id, data); + } + + @Override + public void onSetProtocol(byte protocol) { + mCallback.onSetProtocol(protocol); + } + + @Override + public void onIntrData(byte reportId, byte[] data) { + mCallback.onIntrData(reportId, data); + } + + @Override + public void onVirtualCableUnplug() { + mCallback.onVirtualCableUnplug(); + } + } + + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + + public void onBluetoothStateChange(boolean up) { + Log.d(TAG, "onBluetoothStateChange: up=" + up); + synchronized (mConnection) { + if (!up) { + Log.d(TAG,"Unbinding service..."); + if (mService != null) { + mService = null; + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + Log.e(TAG,"onBluetoothStateChange: could not unbind service:", e); + } + } + } else { + try { + if (mService == null) { + Log.d(TAG,"Binding HID Device service..."); + doBind(); + } + } catch (IllegalStateException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e); + } catch (SecurityException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e); + } + } + } + } + }; + + private ServiceConnection mConnection = new ServiceConnection() { + + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "onServiceConnected()"); + + mService = IBluetoothHidDevice.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, + BluetoothHidDevice.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "onServiceDisconnected()"); + + mService = null; + + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); + } + } + }; + + BluetoothHidDevice(Context context, ServiceListener listener) { + Log.v(TAG, "BluetoothHidDevice"); + + mContext = context; + mServiceListener = listener; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothHidDevice.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { + Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent); + return false; + } + Log.d(TAG, "Bound to HID Device Service"); + return true; + } + + void close() { + Log.v(TAG, "close()"); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + synchronized (mConnection) { + if (mService != null) { + mService = null; + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + Log.e(TAG,"close: could not unbind HID Dev service: ", e); + } + } + } + + mServiceListener = null; + } + + @Override + public List getConnectedDevices() { + Log.v(TAG, "getConnectedDevices()"); + return null; + } + + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); + return null; + } + + @Override + public int getConnectionState(BluetoothDevice device) { + Log.v(TAG, "getConnectionState(): device=" + device.getAddress()); + + return STATE_DISCONNECTED; + } + + /** + * Registers application to be used for HID device. Connections to HID + * Device are only possible when application is registered. Only one + * application can be registered at time. When no longer used, application + * should be unregistered using + * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}. + * + * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of + * HID Device SDP record. + * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of + * Incoming QoS Settings. + * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of + * Outgoing QoS Settings. + * @param callback {@link BluetoothHidDeviceCallback} object to which + * callback messages will be sent. + * @return + */ + public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, + BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, + BluetoothHidDeviceCallback callback) { + Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos + + " callback=" + callback); + + boolean result = false; + + if (sdp == null || callback == null) { + return false; + } + + if (mService != null) { + try { + BluetoothHidDeviceAppConfiguration config = + new BluetoothHidDeviceAppConfiguration(); + BluetoothHidDeviceCallbackWrapper cbw = + new BluetoothHidDeviceCallbackWrapper(callback); + result = mService.registerApp(config, sdp, inQos, outQos, cbw); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Unregisters application. Active connection will be disconnected and no + * new connections will be allowed until registered again using + * {@link #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback)} + * + * @param config {@link BluetoothHidDeviceAppConfiguration} object as + * obtained from + * {@link BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, + * BluetoothHidDeviceAppConfiguration, boolean)} + * + * @return + */ + public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { + Log.v(TAG, "unregisterApp()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.unregisterApp(config); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends report to remote host using interrupt channel. + * + * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id + * are not defined in descriptor. + * @param data Report data, not including Report Id. + * @return + */ + public boolean sendReport(int id, byte[] data) { + Log.v(TAG, "sendReport(): id=" + id); + + boolean result = false; + + if (mService != null) { + try { + result = mService.sendReport(id, data); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends report to remote host as reply for GET_REPORT request from + * {@link BluetoothHidDeviceCallback#onGetReport(byte, byte, int)}. + * + * @param type Report Type, as in request. + * @param id Report Id, as in request. + * @param data Report data, not including Report Id. + * @return + */ + public boolean replyReport(byte type, byte id, byte[] data) { + Log.v(TAG, "replyReport(): type=" + type + " id=" + id); + + boolean result = false; + + if (mService != null) { + try { + result = mService.replyReport(type, id, data); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends error handshake message as reply for invalid SET_REPORT request + * from {@link BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])}. + * + * @param error Error to be sent for SET_REPORT via HANDSHAKE. + * @return + */ + public boolean reportError(byte error) { + Log.v(TAG, "reportError(): error = " + error); + + boolean result = false; + + if (mService != null) { + try { + result = mService.reportError(error); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends Virtual Cable Unplug to currently connected host. + * + * @return + */ + public boolean unplug() { + Log.v(TAG, "unplug()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.unplug(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Initiates connection to host which currently has Virtual Cable + * established with device. + * + * @return + */ + public boolean connect() { + Log.v(TAG, "connect()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.connect(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Disconnects from currently connected host. + * + * @return + */ + public boolean disconnect() { + Log.v(TAG, "disconnect()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.disconnect(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } +} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl new file mode 100644 index 0000000000000..1af309c67ca67 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl @@ -0,0 +1,21 @@ +/* +** Copyright (C) 2013 The Linux Foundation. All rights reserved +** Not a Contribution. +** Copyright 2011, 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.bluetooth; + +parcelable BluetoothHidDeviceAppConfiguration; diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java new file mode 100644 index 0000000000000..9f3cd3c53716d --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppConfiguration implements Parcelable { + private final long mHash; + + BluetoothHidDeviceAppConfiguration() { + Random rnd = new Random(); + mHash = rnd.nextLong(); + } + + BluetoothHidDeviceAppConfiguration(long hash) { + mHash = hash; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppConfiguration) { + BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o; + return mHash == config.mHash; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) { + long hash = in.readLong(); + return new BluetoothHidDeviceAppConfiguration(hash); + } + + @Override + public BluetoothHidDeviceAppConfiguration[] newArray(int size) { + return new BluetoothHidDeviceAppConfiguration[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mHash); + } +} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl new file mode 100644 index 0000000000000..ae93235cc8bd6 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl @@ -0,0 +1,21 @@ +/* +** Copyright (C) 2013 The Linux Foundation. All rights reserved +** Not a Contribution. +** Copyright 2011, 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.bluetooth; + +parcelable BluetoothHidDeviceAppQosSettings; diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java new file mode 100644 index 0000000000000..a4044d935337a --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppQosSettings implements Parcelable { + + final public int serviceType; + final public int tokenRate; + final public int tokenBucketSize; + final public int peakBandwidth; + final public int latency; + final public int delayVariation; + + final static public int SERVICE_NO_TRAFFIC = 0x00; + final static public int SERVICE_BEST_EFFORT = 0x01; + final static public int SERVICE_GUARANTEED = 0x02; + + final static public int MAX = (int) 0xffffffff; + + public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize, + int peakBandwidth, + int latency, int delayVariation) { + this.serviceType = serviceType; + this.tokenRate = tokenRate; + this.tokenBucketSize = tokenBucketSize; + this.peakBandwidth = peakBandwidth; + this.latency = latency; + this.delayVariation = delayVariation; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppQosSettings) { + BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o; + return false; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) { + + return new BluetoothHidDeviceAppQosSettings(in.readInt(), in.readInt(), in.readInt(), + in.readInt(), + in.readInt(), in.readInt()); + } + + @Override + public BluetoothHidDeviceAppQosSettings[] newArray(int size) { + return new BluetoothHidDeviceAppQosSettings[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(serviceType); + out.writeInt(tokenRate); + out.writeInt(tokenBucketSize); + out.writeInt(peakBandwidth); + out.writeInt(latency); + out.writeInt(delayVariation); + } + + public int[] toArray() { + return new int[] { + serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation + }; + } +} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl new file mode 100644 index 0000000000000..38ac1ec6950d9 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl @@ -0,0 +1,21 @@ +/* +** Copyright (C) 2013 The Linux Foundation. All rights reserved +** Not a Contribution. +** Copyright 2011, 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.bluetooth; + +parcelable BluetoothHidDeviceAppSdpSettings; diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java new file mode 100644 index 0000000000000..db88f0d74c038 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { + + final public String name; + final public String description; + final public String provider; + final public byte subclass; + final public byte[] descriptors; + + public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider, + byte subclass, byte[] descriptors) { + this.name = name; + this.description = description; + this.provider = provider; + this.subclass = subclass; + this.descriptors = descriptors.clone(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppSdpSettings) { + BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o; + return false; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) { + + return new BluetoothHidDeviceAppSdpSettings(in.readString(), in.readString(), + in.readString(), in.readByte(), in.createByteArray()); + } + + @Override + public BluetoothHidDeviceAppSdpSettings[] newArray(int size) { + return new BluetoothHidDeviceAppSdpSettings[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(name); + out.writeString(description); + out.writeString(provider); + out.writeByte(subclass); + out.writeByteArray(descriptors); + } +} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java new file mode 100644 index 0000000000000..cc608339b4e88 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.util.Log; + +/** @hide */ +public abstract class BluetoothHidDeviceCallback { + + private static final String TAG = BluetoothHidDeviceCallback.class.getSimpleName(); + + /** + * Callback called when application registration state changes. Usually it's + * called due to either + * {@link BluetoothHidDevice#registerApp(String, String, String, byte, byte[], + * BluetoothHidDeviceCallback)} + * or + * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * , but can be also unsolicited in case e.g. Bluetooth was turned off in + * which case application is unregistered automatically. + * + * @param pluggedDevice {@link BluetoothDevice} object which represents host + * that currently has Virtual Cable established with device. Only + * valid when application is registered, can be null + * . + * @param config {@link BluetoothHidDeviceAppConfiguration} object which + * represents token required to unregister application using + * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * . + * @param registered true if application is registered, + * false otherwise. + */ + public void onAppStatusChanged(BluetoothDevice pluggedDevice, + BluetoothHidDeviceAppConfiguration config, boolean registered) { + Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + (pluggedDevice == null ? + null : pluggedDevice.toString()) + " registered=" + registered); + } + + /** + * Callback called when connection state with remote host was changed. + * Application can assume than Virtual Cable is established when called with + * {@link BluetoothProfile#STATE_CONNECTED} state. + * + * @param device {@link BluetoothDevice} object representing host device + * which connection state was changed. + * @param state Connection state as defined in {@link BluetoothProfile}. + */ + public void onConnectionStateChanged(BluetoothDevice device, int state) { + Log.d(TAG, "onConnectionStateChanged: device=" + device.toString() + " state=" + state); + } + + /** + * Callback called when GET_REPORT is received from remote host. Should be + * replied by application using + * {@link BluetoothHidDevice#replyReport(byte, byte, byte[])}. + * + * @param type Requested Report Type. + * @param id Requested Report Id, can be 0 if no Report Id are defined in + * descriptor. + * @param bufferSize Requested buffer size, application shall respond with + * at least given number of bytes. + */ + public void onGetReport(byte type, byte id, int bufferSize) { + Log.d(TAG, "onGetReport: type=" + type + " id=" + id + " bufferSize=" + bufferSize); + } + + /** + * Callback called when SET_REPORT is received from remote host. In case + * received data are invalid, application shall respond with + * {@link BluetoothHidDevice#reportError()}. + * + * @param type Report Type. + * @param id Report Id. + * @param data Report data. + */ + public void onSetReport(byte type, byte id, byte[] data) { + Log.d(TAG, "onSetReport: type=" + type + " id=" + id); + } + + /** + * Callback called when SET_PROTOCOL is received from remote host. + * Application shall use this information to send only reports valid for + * given protocol mode. By default, + * {@link BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. + * + * @param protocol Protocol Mode. + */ + public void onSetProtocol(byte protocol) { + Log.d(TAG, "onSetProtocol: protocol=" + protocol); + } + + /** + * Callback called when report data is received over interrupt channel. + * Report Type is assumed to be + * {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. + * + * @param reportId Report Id. + * @param data Report data. + */ + public void onIntrData(byte reportId, byte[] data) { + Log.d(TAG, "onIntrData: reportId=" + reportId); + } + + /** + * Callback called when Virtual Cable is removed. This can be either due to + * {@link BluetoothHidDevice#unplug()} or request from remote side. After + * this callback is received connection will be disconnected automatically. + */ + public void onVirtualCableUnplug() { + Log.d(TAG, "onVirtualCableUnplug"); + } +} diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index 252e3d28a25e8..db23ef5489980 100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -96,6 +96,12 @@ public final class BluetoothInputDevice implements BluetoothProfile { public static final String ACTION_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_IDLE_TIME_CHANGED = + "codeaurora.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; /** * Return codes for the connect and disconnect Bluez / Dbus calls. @@ -199,6 +205,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + /** + * @hide + */ + public static final String EXTRA_IDLE_TIME = "codeaurora.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; + private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -658,6 +669,56 @@ public boolean sendData(BluetoothDevice device, String report) { if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; } + + /** + * Send Get_Idle_Time command to the connected HID input device. + * + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean getIdleTime(BluetoothDevice device) { + if (DBG) log("getIdletime(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getIdleTime(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Send Set_Idle_Time command to the connected HID input device. + * + *

Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @param idleTime Idle time to be set on HID Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean setIdleTime(BluetoothDevice device, byte idleTime) { + if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.setIdleTime(device, idleTime); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index cbce22cdea60a..9ef931ea26ed0 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -130,6 +130,18 @@ public interface BluetoothProfile { */ public static final int HEADSET_CLIENT = 16; + /** + * HID device + * @hide + */ + public static final int HID_DEVICE = 17; + + /** + * DUN + * @hide + */ + public static final int DUN = 21; + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index fb81fd1ea9d0d..2eb49531b599a 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -247,6 +247,7 @@ private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { as.mSocketOS = as.mSocket.getOutputStream(); as.mAddress = RemoteAddr; as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr); + as.mPort = mPort; return as; } /** @@ -468,6 +469,61 @@ public void connect() throws IOException { return acceptedSocket; } + /** + * setSocketOpt for the Buetooth Socket. + * + * @param optionName socket option name + * @param optionVal socket option value + * @param optionLen socket option length + * @return -1 on immediate error, + * 0 otherwise + * @hide + */ + public int setSocketOpt(int optionName, byte [] optionVal, int optionLen) throws IOException { + int ret = 0; + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + if (bluetoothProxy == null) { + Log.e(TAG, "setSocketOpt fail, reason: bluetooth is off"); + return -1; + } + try { + if(VDBG) Log.d(TAG, "setSocketOpt(), mType: " + mType + " mPort: " + mPort); + ret = bluetoothProxy.setSocketOpt(mType, mPort, optionName, optionVal, optionLen); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return -1; + } + return ret; + } + + /** + * getSocketOpt for the Buetooth Socket. + * + * @param optionName socket option name + * @param optionVal socket option value + * @return -1 on immediate error, + * length of returned socket option otherwise + * @hide + */ + public int getSocketOpt(int optionName, byte [] optionVal) throws IOException { + int ret = 0; + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + if (bluetoothProxy == null) { + Log.e(TAG, "getSocketOpt fail, reason: bluetooth is off"); + return -1; + } + try { + if(VDBG) Log.d(TAG, "getSocketOpt(), mType: " + mType + " mPort: " + mPort); + ret = bluetoothProxy.getSocketOpt(mType, mPort, optionName, optionVal); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return -1; + } + return ret; + } + /*package*/ int available() throws IOException { if (VDBG) Log.d(TAG, "available: " + mSocketIS); return mSocketIS.available(); diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 66f3418c620f5..2f1e8b4b68071 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -70,6 +70,11 @@ interface IBluetooth boolean fetchRemoteUuids(in BluetoothDevice device); boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid); + int createMapMnsSdpRecord(in String serviceName, in int rfcommChannel, + in int l2capPsm, in int version, in int features); + int createPbapPceSdpRecord(in String serviceName, in int version); + boolean removeSdpRecord(in int recordHandle); + boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] passkey); @@ -106,4 +111,7 @@ interface IBluetooth void dump(in ParcelFileDescriptor fd); void onLeServiceUp(); void onBrEdrDown(); + + int setSocketOpt(int type, int port, int optionName, in byte [] optionVal, int optionLen); + int getSocketOpt(int type, int port, int optionName, out byte [] optionVal); } diff --git a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl old mode 100644 new mode 100755 index b7c6476770009..774a1ecd541b8 --- a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl +++ b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl @@ -30,5 +30,8 @@ interface IBluetoothA2dpSink { List getConnectedDevices(); List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); + boolean isA2dpPlaying(in BluetoothDevice device); BluetoothAudioConfig getAudioConfig(in BluetoothDevice device); } diff --git a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl index f917a50860b78..fb61c98d27e34 100644 --- a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl @@ -1,4 +1,5 @@ /* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,6 +18,7 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothAvrcpInfo; /** * APIs for Bluetooth AVRCP controller service @@ -28,4 +30,10 @@ interface IBluetoothAvrcpController { List getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState); + void getMetaData(in int[] attributeIds); + void getPlayStatus(in int[] playStatusIds); + void getPlayerApplicationSetting(); + void setPlayerApplicationSetting(in int attributeId, in int attribVal); + BluetoothAvrcpInfo getSupportedPlayerAppSetting(in BluetoothDevice device); + int getSupportedFeatures(in BluetoothDevice device); } diff --git a/core/java/android/bluetooth/IBluetoothDun.aidl b/core/java/android/bluetooth/IBluetoothDun.aidl new file mode 100644 index 0000000000000..a4f2017cc81be --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothDun.aidl @@ -0,0 +1,45 @@ +/* +*Copyright (c) 2013, The Linux Foundation. All rights reserved. +* +*Redistribution and use in source and binary forms, with or without +*modification, are permitted provided that the following conditions are +*met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of The Linux Foundation nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +*THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +*WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +*MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +*ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +*BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +*CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +*SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +*BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +*WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +*OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +*IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * API for Bluetooth Dun service + * + * {@hide} + */ +interface IBluetoothDun { + // Public API + boolean disconnect(in BluetoothDevice device); + int getConnectionState(in BluetoothDevice device); + List getConnectedDevices(); + List getDevicesMatchingConnectionStates(in int[] states); +} diff --git a/core/java/android/bluetooth/IBluetoothHidDevice.aidl b/core/java/android/bluetooth/IBluetoothHidDevice.aidl new file mode 100644 index 0000000000000..60358c550312b --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothHidDevice.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHidDeviceAppConfiguration; +import android.bluetooth.IBluetoothHidDeviceCallback; +import android.bluetooth.BluetoothHidDeviceAppSdpSettings; +import android.bluetooth.BluetoothHidDeviceAppQosSettings; + +/** @hide */ +interface IBluetoothHidDevice { + boolean registerApp(in BluetoothHidDeviceAppConfiguration config, + in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos, + in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback); + boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config); + boolean sendReport(in int id, in byte[] data); + boolean replyReport(in byte type, in byte id, in byte[] data); + boolean reportError(byte error); + boolean unplug(); + boolean connect(); + boolean disconnect(); +} diff --git a/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl new file mode 100644 index 0000000000000..7c71a1799c254 --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011, 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.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHidDeviceAppConfiguration; + +/** @hide */ +interface IBluetoothHidDeviceCallback { + void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered); + void onConnectionStateChanged(in BluetoothDevice device, in int state); + void onGetReport(in byte type, in byte id, in int bufferSize); + void onSetReport(in byte type, in byte id, in byte[] data); + void onSetProtocol(in byte protocol); + void onIntrData(in byte reportId, in byte[] data); + void onVirtualCableUnplug(); +} diff --git a/core/java/android/bluetooth/IBluetoothInputDevice.aidl b/core/java/android/bluetooth/IBluetoothInputDevice.aidl index 1ebb9ca6eb12d..5bd3f7819323d 100644 --- a/core/java/android/bluetooth/IBluetoothInputDevice.aidl +++ b/core/java/android/bluetooth/IBluetoothInputDevice.aidl @@ -56,4 +56,12 @@ interface IBluetoothInputDevice { * @hide */ boolean sendData(in BluetoothDevice device, String report); + /** + * @hide + */ + boolean getIdleTime(in BluetoothDevice device); + /** + * @hide + */ + boolean setIdleTime(in BluetoothDevice device, byte idleTime); } diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl index 0b81ee8c547e3..bd8c6c9687f1a 100644 --- a/core/java/android/bluetooth/IBluetoothManager.aidl +++ b/core/java/android/bluetooth/IBluetoothManager.aidl @@ -19,7 +19,6 @@ package android.bluetooth; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManagerCallback; -import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; /** @@ -34,14 +33,11 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String callingPackage); boolean enableNoAutoConnect(); boolean disable(boolean persist); IBluetoothGatt getBluetoothGatt(); - boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - String getAddress(); String getName(); diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index 2260d7e07e035..bf0c48dc7284b 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -16,6 +16,7 @@ package android.content; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityThread; import android.app.IActivityManager; @@ -747,6 +748,17 @@ public int getSendingUserId() { return mPendingResult.mSendingUser; } + /** @hide */ + public String getSendingPackage(Intent intent) { + final IActivityManager mgr = ActivityManagerNative.getDefault(); + try { + boolean fg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0; + return mgr.getCallingPackageForBroadcast(fg); + } catch (RemoteException ex) { + return null; + } + } + /** * Control inclusion of debugging help for mismatched * calls to {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 6ede29b9ba156..863ca659c9324 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -431,6 +431,7 @@ public void appNotRespondingViaProvider(IContentProvider icp) { public final @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { + android.util.SeempLog.record_uri(13, uri); return query(uri, projection, selection, selectionArgs, sortOrder, null); } @@ -471,6 +472,7 @@ public void appNotRespondingViaProvider(IContentProvider icp) { public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) { + android.util.SeempLog.record_uri(13, uri); Preconditions.checkNotNull(uri, "uri"); IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { @@ -984,6 +986,7 @@ public void appNotRespondingViaProvider(IContentProvider icp) { stableProvider = acquireProvider(uri); } releaseUnstableProvider(unstableProvider); + unstableProvider = null; ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( fd.getParcelFileDescriptor(), stableProvider); @@ -1128,6 +1131,7 @@ public void appNotRespondingViaProvider(IContentProvider icp) { stableProvider = acquireProvider(uri); } releaseUnstableProvider(unstableProvider); + unstableProvider = null; ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( fd.getParcelFileDescriptor(), stableProvider); @@ -1221,6 +1225,7 @@ public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException * @return the URL of the newly created row. */ public final @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues values) { + android.util.SeempLog.record_uri(37, url); Preconditions.checkNotNull(url, "url"); IContentProvider provider = acquireProvider(url); if (provider == null) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 758b6fffb1a73..7ddda110bf1bc 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -497,6 +497,9 @@ public int getThemeResId() { @ViewDebug.ExportedProperty(deepExport = true) public abstract Resources.Theme getTheme(); + /** @hide */ + public abstract void recreateTheme(); + /** * Retrieve styled attribute information in this Context's theme. See * {@link android.content.res.Resources.Theme#obtainStyledAttributes(int[])} @@ -3857,6 +3860,26 @@ public abstract Context createPackageContextAsUser( public abstract Context createApplicationContext(ApplicationInfo application, int flags) throws PackageManager.NameNotFoundException; + /** + * Similar to {@link #createPackageContext(String, int)}, but with a + * different {@link UserHandle}. For example, {@link #getContentResolver()} + * will open any {@link Uri} as the given user. A theme package can be + * specified which will be used when adding resources to this context + * + * @hide + */ + public abstract Context createPackageContextAsUser( + String packageName, String themePackageName, int flags, UserHandle user) + throws PackageManager.NameNotFoundException; + + /** + * Creates a context given an {@link android.content.pm.ApplicationInfo}. + * + * @hide + */ + public abstract Context createApplicationContext(ApplicationInfo application, + String themePackageName, int flags) throws PackageManager.NameNotFoundException; + /** * Get the userId associated with this context * @return user id diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 8359edff07618..795b9aeef89ed 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -123,6 +123,12 @@ public Resources.Theme getTheme() { return mBase.getTheme(); } + /** @hide */ + @Override + public void recreateTheme() { + mBase.recreateTheme(); + } + @Override public ClassLoader getClassLoader() { return mBase.getClassLoader(); @@ -757,7 +763,20 @@ public Context createPackageContextAsUser(String packageName, int flags, UserHan @Override public Context createApplicationContext(ApplicationInfo application, int flags) throws PackageManager.NameNotFoundException { - return mBase.createApplicationContext(application, flags); + return createApplicationContext(application, null, flags); + } + + /** @hide */ + public Context createApplicationContext(ApplicationInfo application, + String themePackageName, int flags) throws PackageManager.NameNotFoundException { + return mBase.createApplicationContext(application, themePackageName, flags); + } + + /** @hide */ + @Override + public Context createPackageContextAsUser(String packageName, String themePackageName, + int flags, UserHandle user) throws PackageManager.NameNotFoundException { + return mBase.createPackageContextAsUser(packageName, themePackageName, flags, user); } /** @hide */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 5abf047b8afbd..c06f98a05ff66 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1845,6 +1846,15 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend * dialogs are the notification window-shade and the recent tasks dialog. */ public static final String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS"; + /** + * Broadcast Action: Update preferences for the power menu dialog. This is to provide a + * way for the preferences that need to be enabled/disabled to update because they were + * toggled elsewhere in the settings (ie profiles, immersive desktop, etc) so we don't have + * to do constant lookups while we wait for the menu to be created. Getting the values once + * when necessary is enough. + *@hide + */ + public static final String UPDATE_POWER_MENU = "android.intent.action.UPDATE_POWER_MENU"; /** * Broadcast Action: Trigger the download and eventual installation * of a package. @@ -2121,6 +2131,15 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend */ @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED"; + + /** + * Broadcast Action: The current keyguard wallpaper configuration + * has changed and should be re-read. + * {@hide} + */ + public static final String ACTION_KEYGUARD_WALLPAPER_CHANGED = + "android.intent.action.KEYGUARD_WALLPAPER_CHANGED"; + /** * Broadcast Action: The current device {@link android.content.res.Configuration} * (orientation, locale, etc) has changed. When such a change happens, the @@ -2704,6 +2723,45 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend public static final String ACTION_GET_RESTRICTION_ENTRIES = "android.intent.action.GET_RESTRICTION_ENTRIES"; + /** + *

Broadcast Action: The state of the HOTWORD audio input has changed.:

+ *
    + *
  • state - A String value indicating the state of the input. + * {@link #EXTRA_HOTWORD_INPUT_STATE}. The value will be one of: + * {@link android.media.AudioRecord#RECORDSTATE_RECORDING} or + * {@link android.media.AudioRecord#RECORDSTATE_STOPPED}. + *
  • + *
  • package - A String value indicating the package name of the application + * that currently holds the HOTWORD input. + * {@link #EXTRA_CURRENT_PACKAGE_NAME} + *
  • + *
+ * + *

This is a protected intent that can only be sent + * by the system. It can only be received by packages that hold + * {@link android.Manifest.permission#CAPTURE_AUDIO_HOTWORD}. + * + * @hide + */ + //@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_HOTWORD_INPUT_CHANGED + = "com.cyanogenmod.intent.action.HOTWORD_INPUT_CHANGED"; + + /** + * @hide + * Activity to challenge the user for a PIN that was configured when setting up + * restrictions. Restrictions include blocking of apps and preventing certain user operations, + * controlled by {@link android.os.UserManager#setUserRestrictions(Bundle). + * Launch the activity using + * {@link android.app.Activity#startActivityForResult(Intent, int)} and check if the + * result is {@link android.app.Activity#RESULT_OK} for a successful response to the + * challenge.

+ * Before launching this activity, make sure that there is a PIN in effect, by calling + * {@link android.os.UserManager#hasRestrictionsChallenge()}. + */ + public static final String ACTION_RESTRICTIONS_CHALLENGE = + "android.intent.action.RESTRICTIONS_CHALLENGE"; + /** * Sent the first time a user is starting, to allow system apps to * perform one time initialization. (This will not be seen by third @@ -2875,6 +2933,13 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend public static final String ACTION_SHOW_BRIGHTNESS_DIALOG = "android.intent.action.SHOW_BRIGHTNESS_DIALOG"; + /** + * Activity Action: Shows the notification brightness setting dialog. + * @hide + */ + public static final String ACTION_SHOW_NOTIFICATION_BRIGHTNESS_DIALOG = + "android.intent.action.SHOW_NOTIFICATION_BRIGHTNESS_DIALOG"; + /** * Broadcast Action: A global button was pressed. Includes a single * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that @@ -2963,6 +3028,19 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT"; + /** + * Broadcast Action: A theme's resources were cached. Includes two extra fields, + * {@link #EXTRA_THEME_PACKAGE_NAME}, containing the package name of the theme that was + * processed, and {@link #EXTRA_THEME_RESULT}, containing the result code. + * + *

This is a protected intent that can only be sent + * by the system.

+ * + * @hide + */ + public static final String ACTION_THEME_RESOURCES_CACHED = + "android.intent.action.THEME_RESOURCES_CACHED"; + /** * Activity Action: Allow the user to pick a directory subtree. When * invoked, the system will display the various {@link DocumentsProvider} @@ -3030,6 +3108,10 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY"; + /** {@hide} */ + public static final String ACTION_DOZE_PULSE_STARTING = + "android.intent.action.DOZE_PULSE_STARTING"; + /** * Broadcast action: reports when a new thermal event has been reached. When the device * is reaching its maximum temperatue, the thermal level reported @@ -3881,6 +3963,42 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend public static final String EXTRA_SIM_ACTIVATION_RESPONSE = "android.intent.extra.SIM_ACTIVATION_RESPONSE"; + /** + * Extra for {@link #ACTION_THEME_RESOURCES_CACHED} that provides the return value + * from processThemeResources. A value of 0 indicates a successful caching of resources. + * Error results are: + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_AAPT_ERROR} + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_IDMAP_ERROR} + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_UNKNOWN_ERROR} + * + * @hide + */ + public static final String EXTRA_THEME_RESULT = "android.intent.extra.RESULT"; + + /** + * Extra for {@link #ACTION_THEME_RESOURCES_CACHED} that provides the package name of the + * theme that was processed. + * + * @hide + */ + public static final String EXTRA_THEME_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"; + + /** + * Extra for {@link #ACTION_HOTWORD_INPUT_CHANGED} that provides the state of + * the input when the broadcast action was sent. + * @hide + */ + public static final String EXTRA_HOTWORD_INPUT_STATE = + "com.cyanogenmod.intent.extra.HOTWORD_INPUT_STATE"; + + /** + * Extra for {@link #ACTION_RECENTS_LONG_PRESS} that provides the package name of the + * app in foreground when recents was long pressed. Can be reused for other purposes. + * @hide + */ + public static final String EXTRA_CURRENT_PACKAGE_NAME = + "com.cyanogenmod.intent.extra.CURRENT_PACKAGE_NAME"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 3a17e23b0547a..ed5dfa5393ecb 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -883,6 +883,15 @@ public boolean match(AuthorityEntry other) { return true; } + @Override + public boolean equals(Object obj) { + if (obj instanceof AuthorityEntry) { + final AuthorityEntry other = (AuthorityEntry)obj; + return match(other); + } + return false; + } + /** * Determine whether this AuthorityEntry matches the given data Uri. * Note that this comparison is case-sensitive, unlike formal @@ -917,7 +926,7 @@ public int match(Uri data) { } return MATCH_CATEGORY_HOST; } - }; + } /** * Add a new Intent data "scheme specific part" to match against. The filter must diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java index a586d6fc96ae9..ab3c30bb2d2a3 100644 --- a/core/java/android/content/SyncInfo.java +++ b/core/java/android/content/SyncInfo.java @@ -24,6 +24,13 @@ * Information about the sync operation that is currently underway. */ public class SyncInfo implements Parcelable { + /** + * Used when the caller receiving this object doesn't have permission to access the accounts + * on device. + * @See Manifest.permission.GET_ACCOUNTS + */ + private static final Account REDACTED_ACCOUNT = new Account("*****", "*****"); + /** @hide */ public final int authorityId; @@ -44,6 +51,17 @@ public class SyncInfo implements Parcelable { */ public final long startTime; + /** + * Creates a SyncInfo object with an unusable Account. Used when the caller receiving this + * object doesn't have access to the accounts on the device. + * @See Manifest.permission.GET_ACCOUNTS + * @hide + */ + public static SyncInfo createAccountRedacted( + int authorityId, String authority, long startTime) { + return new SyncInfo(authorityId, REDACTED_ACCOUNT, authority, startTime); + } + /** @hide */ public SyncInfo(int authorityId, Account account, String authority, long startTime) { this.authorityId = authorityId; diff --git a/core/java/android/content/ThemeVersion.java b/core/java/android/content/ThemeVersion.java new file mode 100644 index 0000000000000..05fbc418b016d --- /dev/null +++ b/core/java/android/content/ThemeVersion.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content; + +/** + * Warning: Careful moving/refactoring this class as our SDK references it. + * ThemeVersion 1 = CM11 + * ThemeVersion 2 = CM12/CM12.1 First Release + * ThemeVersion 3 = CM12.1 W/ Wallpaper Packs + * @hide + */ +public class ThemeVersion { + /** + * Increment this anytime changes are made to: + * 1) Changes to ThemesContract + * 2) Changes to ThemeService API + * 3) Changes to ThemeManager API + */ + public static int THEME_VERSION = 3; + + /** + * Change this if a change to the contract or service would break compatibility. + * Example: A client app like chooser might be outdated from the framework. + * It could then query the FW for this value and determine whether its safe to proceed. + */ + public static int MIN_SUPPORTED_THEME_VERSION = 2; + + /** + * Do not change the order of this. See SDK. + * Increment the minSupportedVersion when the fw can no longer support a theme's apk structure + * Increment currentVersion when a change to the theme's apk structure is changed + * For example, CM11 to CM12 introduces new resources to overlay, so the overlays + * version should change. Because the changes are not compatible with CM11, the minVersion + * must change as well. + * + * If a new feature is added to a component (ex rotations in icon packs), the current version + * for the ICON component would be incremented. If a new component is created, then add it + * to the enum list. + * + * Wallpaper Version 2: Multi wallpaper ability + * + */ + public static enum ComponentVersion { + OVERLAY(0, 2, 2), + BOOT_ANIM(1, 1, 1), + WALLPAPER(2, 1, 2), + LOCKSCREEN(3, 1, 1), + FONT(4, 1, 2), + ICON(5, 1, 1), + SOUNDS(6, 1, 1); + + public int id; + public int minSupportedVersion; + public int currentVersion; + + private ComponentVersion(int id, int minSupportedVersion, int currentVersion) { + this.id = id; + this.minSupportedVersion = minSupportedVersion; + this.currentVersion = currentVersion; + } + } +} diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index e798eb8f7d2c2..0105e094d1cf6 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2007 The Android Open Source Project - * + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. + * 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 @@ -544,6 +545,16 @@ public class ActivityInfo extends ComponentInfo * {@link android.R.attr#configChanges} attribute. */ public static final int CONFIG_LAYOUT_DIRECTION = 0x2000; + /** + * Bit in {@link #configChanges} that indicates a theme change occurred + * @hide + */ + public static final int CONFIG_THEME_RESOURCE = 0x100000; + /** + * Bit in {@link #configChanges} that indicates a font change occurred + * @hide + */ + public static final int CONFIG_THEME_FONT = 0x200000; /** * Bit in {@link #configChanges} that indicates that the activity * can itself handle changes to the font scaling factor. Set from the diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 6feb860367912..1933fc99cb982 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -654,6 +655,19 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public int installLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; + /** + * When true, indicates that any one component within this application is + * protected. + * @hide + */ + public boolean protect = false; + + /** + * Is given application theme agnostic, i.e. behaves properly when default theme is changed. + * @hide + */ + public boolean isThemeable = false; + public void dump(Printer pw, String prefix) { super.dumpFront(pw, prefix); if (className != null) { @@ -785,6 +799,8 @@ public ApplicationInfo(ApplicationInfo orig) { uiOptions = orig.uiOptions; backupAgentName = orig.backupAgentName; fullBackupContent = orig.fullBackupContent; + protect = orig.protect; + isThemeable = orig.isThemeable; } @@ -838,6 +854,8 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(descriptionRes); dest.writeInt(uiOptions); dest.writeInt(fullBackupContent); + dest.writeInt(protect ? 1 : 0); + dest.writeInt(isThemeable ? 1 : 0); } public static final Parcelable.Creator CREATOR @@ -890,6 +908,8 @@ private ApplicationInfo(Parcel source) { descriptionRes = source.readInt(); uiOptions = source.readInt(); fullBackupContent = source.readInt(); + protect = source.readInt() != 0; + isThemeable = source.readInt() != 0; } /** diff --git a/core/java/android/content/pm/BaseThemeInfo.java b/core/java/android/content/pm/BaseThemeInfo.java new file mode 100644 index 0000000000000..8ece42d1933d3 --- /dev/null +++ b/core/java/android/content/pm/BaseThemeInfo.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2010, T-Mobile USA, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.os.Parcelable; +import android.os.Parcel; +import android.util.Log; +import android.util.AttributeSet; +import android.content.res.Resources; + +/** + * @hide + */ +public class BaseThemeInfo implements Parcelable { + /** + * The theme id, which does not change when the theme is modified. + * Specifies an Android UI Style using style name. + * + * @see themeId attribute + * + */ + public String themeId; + + /** + * The name of the theme (as displayed by UI). + * + * @see name attribute + * + */ + public String name; + + /** + * The author name of the theme package. + * + * @see author attribute + * + */ + public String author; + + /* + * Describe the kinds of special objects contained in this Parcelable's + * marshalled representation. + * + * @return a bitmask indicating the set of special object types marshalled + * by the Parcelable. + * + * @see android.os.Parcelable#describeContents() + */ + public int describeContents() { + return 0; + } + + /* + * Flatten this object in to a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. + * + * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(themeId); + dest.writeString(name); + dest.writeString(author); + } + + /** @hide */ + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public BaseThemeInfo createFromParcel(Parcel source) { + return new BaseThemeInfo(source); + } + + public BaseThemeInfo[] newArray(int size) { + return new BaseThemeInfo[size]; + } + }; + + /** @hide */ + public final String getResolvedString(Resources res, AttributeSet attrs, int index) { + int resId = attrs.getAttributeResourceValue(index, 0); + if (resId !=0 ) { + return res.getString(resId); + } + return attrs.getAttributeValue(index); + } + + protected BaseThemeInfo() { + } + + protected BaseThemeInfo(Parcel source) { + themeId = source.readString(); + name = source.readString(); + author = source.readString(); + } +} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index fec2c4493671c..51f13affddeb5 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -17,6 +17,7 @@ package android.content.pm; +import android.app.ComposedIconInfo; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -509,4 +510,20 @@ interface IPackageManager { boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId); String getPermissionControllerPackageName(); + + /** Protected Apps */ + void setComponentProtectedSetting(in ComponentName componentName, + in boolean newState, int userId); + + /** Themes */ + void updateIconMapping(String pkgName); + ComposedIconInfo getComposedIconInfo(); + int processThemeResources(String themePkgName); + + /** Protected Apps */ + boolean isComponentProtected(in String callingPackage, in int callingUid, + in ComponentName componentName, int userId); + + /** protected broadcast ext */ + boolean isProtectedBroadcastAllowed(in String actionName, in int callingUid); } diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 9e6c6b501f62d..0de867e347c18 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +17,11 @@ package android.content.pm; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import android.os.Parcel; import android.os.Parcelable; @@ -254,6 +260,34 @@ public class PackageInfo implements Parcelable { /** @hide */ public boolean coreApp; + // Is Theme Apk + /** + * {@hide} + */ + public boolean isThemeApk = false; + + /** + * {@hide} + */ + public boolean hasIconPack = false; + + /** + * {@hide} + */ + public ArrayList mOverlayTargets; + + // Is Legacy Icon Apk + /** + * {@hide} + */ + public boolean isLegacyIconPackApk = false; + + // ThemeInfo + /** + * {@hide} + */ + public ThemeInfo themeInfo; + /** @hide */ public boolean requiredForAllUsers; @@ -323,6 +357,13 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeString(restrictedAccountType); dest.writeString(requiredAccountType); dest.writeString(overlayTarget); + + /* Theme-specific. */ + dest.writeInt((isThemeApk) ? 1 : 0); + dest.writeStringList(mOverlayTargets); + dest.writeParcelable(themeInfo, parcelableFlags); + dest.writeInt(hasIconPack ? 1 : 0); + dest.writeInt((isLegacyIconPackApk) ? 1 : 0); } public static final Parcelable.Creator CREATOR @@ -372,5 +413,12 @@ private PackageInfo(Parcel source) { restrictedAccountType = source.readString(); requiredAccountType = source.readString(); overlayTarget = source.readString(); + + /* Theme-specific. */ + isThemeApk = (source.readInt() != 0); + mOverlayTargets = source.createStringArrayList(); + themeInfo = source.readParcelable(null); + hasIconPack = source.readInt() == 1; + isLegacyIconPackApk = source.readInt() == 1; } } diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java index 1efe082b7fdf9..d4f33fbed6dd3 100644 --- a/core/java/android/content/pm/PackageInfoLite.java +++ b/core/java/android/content/pm/PackageInfoLite.java @@ -62,6 +62,7 @@ public class PackageInfoLite implements Parcelable { */ public int recommendedInstallLocation; public int installLocation; + public boolean isTheme; public VerifierInfo[] verifiers; @@ -87,6 +88,7 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(recommendedInstallLocation); dest.writeInt(installLocation); dest.writeInt(multiArch ? 1 : 0); + dest.writeInt(isTheme ? 1 : 0); if (verifiers == null || verifiers.length == 0) { dest.writeInt(0); @@ -116,6 +118,7 @@ private PackageInfoLite(Parcel source) { recommendedInstallLocation = source.readInt(); installLocation = source.readInt(); multiArch = (source.readInt() != 0); + isTheme = source.readInt() == 1 ? true : false; final int verifiersLength = source.readInt(); if (verifiersLength == 0) { diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index 22a899cda05b7..366deb4f9ae65 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -66,7 +66,14 @@ public class PackageItemInfo { * component's icon. From the "icon" attribute or, if not set, 0. */ public int icon; - + + /** + * A drawable resource identifier in the icon pack's resources + * If there isn't an icon pack or not set, then 0. + * @hide + */ + public int themedIcon; + /** * A drawable resource identifier (in the package's resources) of this * component's banner. From the "banner" attribute or, if not set, 0. @@ -110,6 +117,7 @@ public PackageItemInfo(PackageItemInfo orig) { logo = orig.logo; metaData = orig.metaData; showUserIcon = orig.showUserIcon; + themedIcon = orig.themedIcon; } /** @@ -309,8 +317,9 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeBundle(metaData); dest.writeInt(banner); dest.writeInt(showUserIcon); + dest.writeInt(themedIcon); } - + protected PackageItemInfo(Parcel source) { name = source.readString(); packageName = source.readString(); @@ -322,6 +331,7 @@ protected PackageItemInfo(Parcel source) { metaData = source.readBundle(); banner = source.readInt(); showUserIcon = source.readInt(); + themedIcon = source.readInt(); } /** diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c8e9402e64424..8f0500e9b8513 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -829,6 +829,51 @@ public interface OnPermissionsChangedListener { /** {@hide} */ public static final int INSTALL_FAILED_ABORTED = -115; + /** + * Used by themes + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the theme because aapt could not compile the app + * @hide + */ + public static final int INSTALL_FAILED_THEME_AAPT_ERROR = -400; + + /** + * Used by themes + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the theme because idmap failed + * apps. + * @hide + */ + public static final int INSTALL_FAILED_THEME_IDMAP_ERROR = -401; + + /** + * Used by themes + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the theme for an unknown reason + * apps. + * @hide + */ + public static final int INSTALL_FAILED_THEME_UNKNOWN_ERROR = -402; + + /** + * Used for prebundles + * Installation failed for a prebundled app because the user previously uninstalled it + * and we don't want to bring it back + * @hide + */ + public static final int INSTALL_FAILED_UNINSTALLED_PREBUNDLE = -403; + + /** + * Used for prebundles + * Installation failed for a prebundled app because it wasn't needed in the default + * mobile country exported by the hardware + * @hide + */ + public static final int INSTALL_FAILED_REGION_LOCKED_PREBUNDLE = -404; //bloat not found + /** * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the * package's data directory. @@ -1911,6 +1956,20 @@ public interface OnPermissionsChangedListener { public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; + /** + * Flag for {@link #setComponentProtectedSetting(android.content.ComponentName, boolean)}: + * This component or application has set to protected status + * @hide + */ + public static final boolean COMPONENT_PROTECTED_STATUS = false; + + /** + * Flag for {@link #setComponentProtectedSetting(android.content.ComponentName, boolean)}: + * This component or application has been explicitly set to visible status + * @hide + */ + public static final boolean COMPONENT_VISIBLE_STATUS = true; + /** * String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of * {@link #INSTALL_FAILED_DUPLICATE_PERMISSION}. This extra names the package which provides @@ -3511,6 +3570,18 @@ public abstract Resources getResourcesForApplication(String appPackageName) public abstract Resources getResourcesForApplicationAsUser(String appPackageName, int userId) throws NameNotFoundException; + /** @hide */ + public abstract Resources getThemedResourcesForApplication(ApplicationInfo app, + String themePkgName) throws NameNotFoundException; + + /** @hide */ + public abstract Resources getThemedResourcesForApplication(String appPackageName, + String themePkgName) throws NameNotFoundException; + + /** @hide */ + public abstract Resources getThemedResourcesForApplicationAsUser(String appPackageName, + String themePkgName, int userId) throws NameNotFoundException; + /** * Retrieve overall information about an application package defined * in a package archive file @@ -4483,6 +4554,19 @@ public void onCreated(int moveId, Bundle extras) {} */ public abstract @NonNull PackageInstaller getPackageInstaller(); + /** + * Update Component protection state + * @hide + */ + public abstract void setComponentProtectedSetting(ComponentName componentName, boolean newState); + + /** + * Return whether or not a specific component is protected + * @hide + */ + public abstract boolean isComponentProtected(String callingPackage, int callingUid, + ComponentName componentName); + /** * Adds a {@link CrossProfileIntentFilter}. After calling this method all intents sent from the * user with id sourceUserId can also be be resolved by activities in the user with id @@ -4709,4 +4793,22 @@ public void onPackageDeleted(String basePackageName, int returnCode, String msg) } } } + + /** + * Updates the theme icon res id for the new theme + * @hide + */ + public abstract void updateIconMaps(String pkgName); + + /** + * Used to compile theme resources for a given theme + * @param themePkgName + * @return A value of 0 indicates success. Possible errors returned are: + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_AAPT_ERROR}, + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_IDMAP_ERROR}, or + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_UNKNOWN_ERROR} + * + * @hide + */ + public abstract int processThemeResources(String themePkgName); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 99bd390353582..bb46ef0b4d14c 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +62,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; @@ -77,12 +79,17 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.jar.StrictJarFile; import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /** * Parser for package files (APKs) on disk. This supports apps packaged either @@ -112,6 +119,17 @@ public class PackageParser { /** File name in an APK for the Android manifest. */ private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; + /** Path to overlay directory in a theme APK */ + private static final String OVERLAY_PATH = "assets/overlays/"; + /** Path to icon directory in a theme APK */ + private static final String ICON_PATH = "assets/icons/"; + + private static final String PACKAGE_REDIRECTIONS_XML = "res/xml/redirections.xml"; + + private static final String TAG_PACKAGE_REDIRECTIONS = "package-redirections"; + private static final String TAG_RESOURCE_REDIRECTIONS = "resource-redirections"; + private static final String TAG_ITEM = "item"; + private static final String ATTRIBUTE_ITEM_NAME = "name"; /** Path prefix for apps on expanded storage */ private static final String MNT_EXPAND = "/mnt/expand/"; @@ -251,6 +269,7 @@ public static class PackageLite { public final int versionCode; public final int installLocation; public final VerifierInfo[] verifiers; + public boolean isTheme; /** Names of any split APKs, ordered by parsed splitName */ public final String[] splitNames; @@ -276,6 +295,7 @@ public static class PackageLite { public final boolean multiArch; public final boolean extractNativeLibs; + public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, String[] splitCodePaths, int[] splitRevisionCodes) { this.packageName = baseApk.packageName; @@ -291,6 +311,7 @@ public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, this.coreApp = baseApk.coreApp; this.multiArch = baseApk.multiArch; this.extractNativeLibs = baseApk.extractNativeLibs; + this.isTheme = baseApk.isTheme; } public List getAllCodePaths() { @@ -318,11 +339,12 @@ public static class ApkLite { public final boolean coreApp; public final boolean multiArch; public final boolean extractNativeLibs; + public final boolean isTheme; public ApkLite(String codePath, String packageName, String splitName, int versionCode, int revisionCode, int installLocation, List verifiers, Signature[] signatures, boolean coreApp, boolean multiArch, - boolean extractNativeLibs) { + boolean extractNativeLibs, boolean isTheme) { this.codePath = codePath; this.packageName = packageName; this.splitName = splitName; @@ -334,6 +356,7 @@ public ApkLite(String codePath, String packageName, String splitName, int versio this.coreApp = coreApp; this.multiArch = multiArch; this.extractNativeLibs = extractNativeLibs; + this.isTheme = isTheme; } } @@ -424,6 +447,14 @@ public static PackageInfo generatePackageInfo(PackageParser.Package p, pi.versionName = p.mVersionName; pi.sharedUserId = p.mSharedUserId; pi.sharedUserLabel = p.mSharedUserLabel; + pi.isThemeApk = p.mIsThemeApk; + pi.hasIconPack = p.hasIconPack; + pi.isLegacyIconPackApk = p.mIsLegacyIconPackApk; + + if (pi.isThemeApk) { + pi.mOverlayTargets = p.mOverlayTargets; + pi.themeInfo = p.mThemeInfo; + } pi.applicationInfo = generateApplicationInfo(p, flags, state, userId); pi.installLocation = p.installLocation; pi.coreApp = p.coreApp; @@ -614,6 +645,7 @@ private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry public final static int PARSE_IS_PRIVILEGED = 1<<7; public final static int PARSE_COLLECT_CERTIFICATES = 1<<8; public final static int PARSE_TRUSTED_OVERLAY = 1<<9; + public final static int PARSE_IS_PREBUNDLED_DIR = 1<<10; private static final Comparator sSplitNameComparator = new SplitNameComparator(); @@ -898,6 +930,18 @@ private Package parseBaseApk(File apkFile, AssetManager assets, int flags) pkg.baseCodePath = apkPath; pkg.mSignatures = null; + // If the pkg is a theme, we need to know what themes it overlays + // and determine if it has an icon pack + if (pkg.mIsThemeApk) { + //Determine existance of Overlays + ArrayList overlayTargets = scanPackageOverlays(apkFile); + for(String overlay : overlayTargets) { + pkg.mOverlayTargets.add(overlay); + } + + pkg.hasIconPack = packageHasIconPack(apkFile); + } + return pkg; } catch (PackageParserException e) { @@ -1020,6 +1064,68 @@ private Package parseSplitApk(Package pkg, Resources res, XmlResourceParser pars return pkg; } + + private ArrayList scanPackageOverlays(File originalFile) { + Set overlayTargets = new HashSet(); + ZipFile privateZip = null; + try { + privateZip = new ZipFile(originalFile.getPath()); + final Enumeration privateZipEntries = privateZip.entries(); + while (privateZipEntries.hasMoreElements()) { + final ZipEntry zipEntry = privateZipEntries.nextElement(); + final String zipEntryName = zipEntry.getName(); + + if (zipEntryName.startsWith(OVERLAY_PATH) && zipEntryName.length() > 16) { + String[] subdirs = zipEntryName.split("/"); + overlayTargets.add(subdirs[2]); + } + } + } catch(Exception e) { + e.printStackTrace(); + overlayTargets.clear(); + } finally { + if (privateZip != null) { + try { + privateZip.close(); + } catch (Exception e) { + //Ignore + } + } + } + + ArrayList overlays = new ArrayList(); + overlays.addAll(overlayTargets); + return overlays; + } + + private boolean packageHasIconPack(File originalFile) { + ZipFile privateZip = null; + try { + privateZip = new ZipFile(originalFile.getPath()); + final Enumeration privateZipEntries = privateZip.entries(); + while (privateZipEntries.hasMoreElements()) { + final ZipEntry zipEntry = privateZipEntries.nextElement(); + final String zipEntryName = zipEntry.getName(); + + if (zipEntryName.startsWith(ICON_PATH) && + zipEntryName.length() > ICON_PATH.length()) { + return true; + } + } + } catch(Exception e) { + Log.e(TAG, "Could not read zip entries while checking if apk has icon pack", e); + } finally { + if (privateZip != null) { + try { + privateZip.close(); + } catch (Exception e) { + //Ignore + } + } + } + return false; + } + /** * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the * APK. If it successfully scanned the package and found the @@ -1027,6 +1133,7 @@ private Package parseSplitApk(Package pkg, Resources res, XmlResourceParser pars */ public void collectManifestDigest(Package pkg) throws PackageParserException { pkg.manifestDigest = null; + pkg.manifestHashCode = 0; // TODO: extend to gather digest for split APKs try { @@ -1035,6 +1142,7 @@ public void collectManifestDigest(Package pkg) throws PackageParserException { final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); + pkg.manifestHashCode = ThemeUtils.getPackageHashCode(pkg, jarFile); } } finally { jarFile.close(); @@ -1300,6 +1408,9 @@ private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParse // Only search the tree when the tag is directly below int type; final int searchDepth = parser.getDepth() + 1; + // Search for category and actions inside + final int iconPackSearchDepth = parser.getDepth() + 4; + boolean isTheme = false; final List verifiers = new ArrayList(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -1326,11 +1437,53 @@ private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParse } } } + + if (parser.getDepth() == searchDepth && "meta-data".equals(parser.getName())) { + for (int i=0; i < parser.getAttributeCount(); i++) { + if ("name".equals(parser.getAttributeName(i)) && + ThemeInfo.META_TAG_NAME.equals(parser.getAttributeValue(i))) { + isTheme = true; + installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; + break; + } + } + } + + if (parser.getDepth() == searchDepth && "theme".equals(parser.getName())) { + isTheme = true; + installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; + } + + if (parser.getDepth() == iconPackSearchDepth && isLegacyIconPack(parser)) { + isTheme = true; + installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; + } } return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode, revisionCode, installLocation, verifiers, signatures, coreApp, multiArch, - extractNativeLibs); + extractNativeLibs, isTheme); + } + + private static boolean isLegacyIconPack(XmlPullParser parser) { + boolean isAction = "action".equals(parser.getName()); + boolean isCategory = "category".equals(parser.getName()); + String[] items = isAction ? ThemeUtils.sSupportedActions + : (isCategory ? ThemeUtils.sSupportedCategories : null); + + if (items != null) { + for (int i = 0; i < parser.getAttributeCount(); i++) { + if ("name".equals(parser.getAttributeName(i))) { + final String value = parser.getAttributeValue(i); + for (String item : items) { + if (item.equals(value)) { + return true; + } + } + } + } + } + return false; } /** @@ -1382,6 +1535,8 @@ private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, } final Package pkg = new Package(pkgName); + Bundle metaDataBundle = new Bundle(); + boolean foundApp = false; TypedArray sa = res.obtainAttributes(attrs, @@ -1722,14 +1877,18 @@ private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name); + String permission = sa.getNonResourceString( + com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_permission); + sa.recycle(); if (name != null && (flags&PARSE_IS_SYSTEM) != 0) { if (pkg.protectedBroadcasts == null) { - pkg.protectedBroadcasts = new ArrayList(); + pkg.protectedBroadcasts = new ArrayMap<>(); } - if (!pkg.protectedBroadcasts.contains(name)) { - pkg.protectedBroadcasts.add(name.intern()); + if (!pkg.protectedBroadcasts.containsKey(name)) { + pkg.protectedBroadcasts.put(name.intern(), + permission != null ? permission.intern() : null); } } @@ -1794,6 +1953,11 @@ private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, XmlUtils.skipCurrentTag(parser); continue; + } else if (parser.getName().equals("meta-data")) { + if ((metaDataBundle=parseMetaData(res, parser, attrs, metaDataBundle, + outError)) == null) { + return null; + } } else if (RIGID_PARSER) { outError[0] = "Bad element under : " + parser.getName(); @@ -1882,6 +2046,17 @@ private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; } + if (pkg.mIsThemeApk || pkg.mIsLegacyIconPackApk) { + pkg.applicationInfo.isThemeable = false; + } + + //Is this pkg a theme? + if (metaDataBundle.containsKey(ThemeInfo.META_TAG_NAME)) { + pkg.mIsThemeApk = true; + pkg.mTrustedOverlay = true; + pkg.mOverlayPriority = 1; + pkg.mThemeInfo = new ThemeInfo(metaDataBundle); + } return pkg; } @@ -2257,6 +2432,10 @@ private Permission parsePermission(Package owner, Resources res, perm.info.flags = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0); + perm.info.allowViaWhitelist = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestPermission_allowViaWhitelist, + false); + sa.recycle(); if (perm.info.protectionLevel == -1) { @@ -2409,6 +2588,10 @@ private boolean parseBaseApplication(Package owner, Resources res, final ApplicationInfo ai = owner.applicationInfo; final String pkgName = owner.applicationInfo.packageName; + String[] nonThemeablePackages = + res.getStringArray(com.android.internal.R.array.non_themeable_packages); + ai.isThemeable = isPackageThemeable(pkgName, nonThemeablePackages); + TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestApplication); @@ -3238,6 +3421,26 @@ private Activity parseActivity(Package owner, Resources res, if (!parseIntent(res, parser, attrs, true, true, intent, outError)) { return null; } + + // Check if package is a legacy icon pack + if (!owner.mIsLegacyIconPackApk) { + for(String action : ThemeUtils.sSupportedActions) { + if (intent.hasAction(action)) { + owner.mIsLegacyIconPackApk = true; + break; + } + + } + } + if (!owner.mIsLegacyIconPackApk) { + for(String category : ThemeUtils.sSupportedCategories) { + if (intent.hasCategory(category)) { + owner.mIsLegacyIconPackApk = true; + break; + } + } + } + if (intent.countActions() == 0) { Slog.w(TAG, "No actions in intent filter at " + mArchiveSourcePath + " " @@ -4247,6 +4450,22 @@ private boolean parseIntent(Resources res, XmlPullParser parser, AttributeSet at return true; } + /**1 + * Returns whether the specified package is themeable + * @param packageName Name of package to check + * @param nonThemeablePackages Array of packages that are declared as non-themeable + * @return True if the package is themeable, false otherwise + */ + private static boolean isPackageThemeable(String packageName, String[] nonThemeablePackages) { + for (String pkg : nonThemeablePackages) { + if (packageName.startsWith(pkg)) { + return false; + } + } + + return true; + } + /** * Representation of a full package parsed from APK files on disk. A package * consists of a single base APK, and zero or more split APKs. @@ -4304,7 +4523,10 @@ public final static class Package { public final ArrayList requestedPermissions = new ArrayList(); - public ArrayList protectedBroadcasts; + /** + * Maps from package -> permission, null for system (default behavior) + */ + public ArrayMap protectedBroadcasts; public ArrayList libraryNames = null; public ArrayList usesLibraries = null; @@ -4345,6 +4567,17 @@ public final static class Package { // For use by package manager to keep track of when a package was last used. public long mLastPackageUsageTimeInMills; + // Is Theme Apk + public boolean mIsThemeApk = false; + public final ArrayList mOverlayTargets = new ArrayList(0); + public Map> mPackageRedirections + = new HashMap>(); + + // Theme info + public ThemeInfo mThemeInfo = null; + + // Legacy icon pack + public boolean mIsLegacyIconPackApk = false; // // User set enabled state. // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; @@ -4387,6 +4620,9 @@ public final static class Package { public int mOverlayPriority; public boolean mTrustedOverlay; + public boolean hasIconPack; + public int manifestHashCode; + /** * Data used to feed the KeySetManagerService */ @@ -4739,6 +4975,12 @@ private static boolean copyNeeded(int flags, Package p, && p.usesLibraryFiles != null) { return true; } + if (state.protectedComponents != null) { + boolean protect = state.protectedComponents.size() > 0; + if (p.applicationInfo.protect != protect) { + return true; + } + } return false; } @@ -4772,6 +5014,9 @@ private static void updateApplicationInfo(ApplicationInfo ai, int flags, ai.enabled = false; } ai.enabledSetting = state.enabled; + if (state.protectedComponents != null) { + ai.protect = state.protectedComponents.size() > 0; + } } public static ApplicationInfo generateApplicationInfo(Package p, int flags, diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 9b28401f5938f..7b6d1884dfd9a 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -36,6 +36,8 @@ public class PackageUserState { public ArraySet disabledComponents; public ArraySet enabledComponents; + public ArraySet protectedComponents; + public ArraySet visibleComponents; public int domainVerificationStatus; public int appLinkGeneration; @@ -62,5 +64,9 @@ public PackageUserState(PackageUserState o) { blockUninstall = o.blockUninstall; domainVerificationStatus = o.domainVerificationStatus; appLinkGeneration = o.appLinkGeneration; + protectedComponents = o.protectedComponents != null + ? new ArraySet(o.protectedComponents) : null; + visibleComponents = o.visibleComponents != null + ? new ArraySet(o.visibleComponents) : null; } } diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 9da2ba9ce59db..0fed65fa32b9e 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -178,6 +178,14 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { */ public CharSequence nonLocalizedDescription; + /** + * Whether this permission will be granted to apps signed with white-listed keys in + * /system/etc/permissions/someapp.xml + * + * @hide + */ + public boolean allowViaWhitelist; + /** @hide */ public static int fixProtectionLevel(int level) { if (level == PROTECTION_SIGNATURE_OR_SYSTEM) { @@ -237,6 +245,7 @@ public PermissionInfo(PermissionInfo orig) { group = orig.group; descriptionRes = orig.descriptionRes; nonLocalizedDescription = orig.nonLocalizedDescription; + allowViaWhitelist = orig.allowViaWhitelist; } /** @@ -279,6 +288,7 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(flags); dest.writeString(group); dest.writeInt(descriptionRes); + dest.writeInt(allowViaWhitelist ? 1 : 0); TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags); } @@ -298,6 +308,7 @@ private PermissionInfo(Parcel source) { flags = source.readInt(); group = source.readString(); descriptionRes = source.readInt(); + allowViaWhitelist = source.readInt() == 1; nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); } } diff --git a/core/java/android/content/pm/ThemeInfo.aidl b/core/java/android/content/pm/ThemeInfo.aidl new file mode 100644 index 0000000000000..acbc85e9c8b98 --- /dev/null +++ b/core/java/android/content/pm/ThemeInfo.aidl @@ -0,0 +1,3 @@ +package android.content.pm; + +parcelable ThemeInfo; diff --git a/core/java/android/content/pm/ThemeInfo.java b/core/java/android/content/pm/ThemeInfo.java new file mode 100644 index 0000000000000..ab798db7b0064 --- /dev/null +++ b/core/java/android/content/pm/ThemeInfo.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010, T-Mobile USA, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParser; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.content.res.Resources; + +/** + * Overall information about "theme" package. This corresponds + * to the information collected from AndroidManifest.xml + * + * Below is an example of the manifest: + * + * + * + * + * @hide + */ +public final class ThemeInfo extends BaseThemeInfo { + + public static final String META_TAG_NAME = "org.cyanogenmod.theme.name"; + public static final String META_TAG_AUTHOR = "org.cyanogenmod.theme.author"; + + public ThemeInfo(Bundle bundle) { + super(); + name = bundle.getString(META_TAG_NAME); + themeId = name; + author = bundle.getString(META_TAG_AUTHOR); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public ThemeInfo createFromParcel(Parcel source) { + return new ThemeInfo(source); + } + + public ThemeInfo[] newArray(int size) { + return new ThemeInfo[size]; + } + }; + + private ThemeInfo(Parcel source) { + super(source); + } +} diff --git a/core/java/android/content/pm/ThemeUtils.java b/core/java/android/content/pm/ThemeUtils.java new file mode 100644 index 0000000000000..07e73b544420a --- /dev/null +++ b/core/java/android/content/pm/ThemeUtils.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.pm; + +import android.content.res.ThemeConfig; +import android.text.TextUtils; + +import java.io.File; +import java.io.IOException; +import java.util.jar.StrictJarFile; +import java.util.zip.ZipEntry; + +/** + * @hide + */ +public class ThemeUtils { + private static final String TAG = ThemeUtils.class.getSimpleName(); + + /* Path inside a theme APK to the overlay folder */ + public static final String OVERLAY_PATH = "assets/overlays/"; + public static final String ICONS_PATH = "assets/icons/"; + public static final String COMMON_RES_PATH = "assets/overlays/common/"; + public static final String RESOURCE_CACHE_DIR = "/data/resource-cache/"; + public static final String COMMON_RES_TARGET = "common"; + + // Package name for any app which does not have a specific theme applied + private static final String DEFAULT_PKG = "default"; + + private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; + + /** + * IDMAP hash version code used to alter the resulting hash and force recreating + * of the idmap. This value should be changed whenever there is a need to force + * an update to all idmaps. + */ + private static final byte IDMAP_HASH_VERSION = 3; + + // Actions in manifests which identify legacy icon packs + public static final String[] sSupportedActions = new String[] { + "org.adw.launcher.THEMES", + "com.gau.go.launcherex.theme", + "com.novalauncher.THEME" + }; + + // Categories in manifests which identify legacy icon packs + public static final String[] sSupportedCategories = new String[] { + "com.fede.launcher.THEME_ICONPACK", + "com.anddoes.launcher.THEME", + "com.teslacoilsw.launcher.THEME" + }; + + + /** + * Get the root path of the resource cache for the given theme + * @param themePkgName + * @return Root resource cache path for the given theme + */ + public static String getOverlayResourceCacheDir(String themePkgName) { + return RESOURCE_CACHE_DIR + themePkgName; + } + + /** + * Get the path of the resource cache for the given target and theme + * @param targetPkgName Target app package name + * @param themePkgName Theme package name + * @return Path to the resource cache for this target and theme + */ + public static String getTargetCacheDir(String targetPkgName, String themePkgName) { + return getOverlayResourceCacheDir(themePkgName) + File.separator + targetPkgName; + } + + /** + * Get the path to the icons for the given theme + * @param pkgName + * @return + */ + public static String getIconPackDir(String pkgName) { + return getOverlayResourceCacheDir(pkgName) + File.separator + "icons"; + } + + public static String getIconPackApkPath(String pkgName) { + return getIconPackDir(pkgName) + "/resources.apk"; + } + + public static String getIdmapPath(String targetPkgName, String overlayPkgName) { + return getTargetCacheDir(targetPkgName, overlayPkgName) + File.separator + "idmap"; + } + + public static String getOverlayPathToTarget(String targetPkgName) { + StringBuilder sb = new StringBuilder(); + sb.append(OVERLAY_PATH); + sb.append(targetPkgName); + sb.append('/'); + return sb.toString(); + } + + public static String getCommonPackageName(String themePackageName) { + if (TextUtils.isEmpty(themePackageName)) return null; + + return COMMON_RES_TARGET; + } + + /** + * Convenience method to determine if a theme component is a per app theme and not a standard + * component. + * @param component + * @return + */ + public static boolean isPerAppThemeComponent(String component) { + return !(DEFAULT_PKG.equals(component) + || ThemeConfig.SYSTEMUI_STATUS_BAR_PKG.equals(component) + || ThemeConfig.SYSTEMUI_NAVBAR_PKG.equals(component)); + } + + /** + * Get a 32 bit hashcode for the given package. + * @param pkg + * @return + */ + public static int getPackageHashCode(PackageParser.Package pkg, StrictJarFile jarFile) { + int hash = pkg.manifestDigest != null ? pkg.manifestDigest.hashCode() : 0; + final ZipEntry je = jarFile.findEntry(MANIFEST_NAME); + if (je != null) { + try { + try { + ManifestDigest digest = ManifestDigest.fromInputStream( + jarFile.getInputStream(je)); + if (digest != null) { + hash += digest.hashCode(); + } + } finally { + jarFile.close(); + } + } catch (IOException | RuntimeException e) { + // Failed to generate digest from manifest.mf + } + } + hash = 31 * hash + IDMAP_HASH_VERSION; + return hash; + } +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 8d96f5c2a9ce7..abb187181deef 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -21,9 +21,11 @@ import android.util.SparseArray; import android.util.TypedValue; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashMap; /** @@ -77,6 +79,16 @@ public final class AssetManager implements AutoCloseable { private boolean mOpen = true; private HashMap mRefStacks; + private String mAppName; + + private boolean mThemeSupport; + private String mThemePackageName; + private String mIconPackageName; + private String mCommonResPackageName; + private ArrayList mThemeCookies = new ArrayList(2); + private int mIconPackCookie; + private int mCommonResCookie; + /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the @@ -252,6 +264,12 @@ public void close() { } } + /*package*/ final void recreateStringBlocks() { + synchronized (this) { + makeStringBlocks(sSystem.mStringBlocks); + } + } + /*package*/ final void makeStringBlocks(StringBlock[] seed) { final int seedNum = (seed != null) ? seed.length : 0; final int num = getStringBlockCount(); @@ -612,7 +630,9 @@ protected void finalize() throws Throwable public final int addAssetPath(String path) { synchronized (this) { int res = addAssetPathNative(path); - makeStringBlocks(mStringBlocks); + if (mStringBlocks != null) { + makeStringBlocks(mStringBlocks); + } return res; } } @@ -627,11 +647,14 @@ public final int addAssetPath(String path) { * * {@hide} */ - - public final int addOverlayPath(String idmapPath) { + public final int addOverlayPath(String idmapPath, String themeApkPath, + String resApkPath, String targetPkgPath, String prefixPath) { synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); + int res = addOverlayPathNative(idmapPath, themeApkPath, resApkPath, targetPkgPath, + prefixPath); + if (mStringBlocks != null) { + makeStringBlocks(mStringBlocks); + } return res; } } @@ -641,7 +664,59 @@ public final int addOverlayPath(String idmapPath) { * * {@hide} */ - public native final int addOverlayPathNative(String idmapPath); + private native final int addOverlayPathNative(String idmapPath, String themeApkPath, + String resApkPath, String targetPkgPath, String prefixPath); + + /** + * Add a set of common assets. + * + * {@hide} + */ + public final int addCommonOverlayPath(String themeApkPath, + String resApkPath, String prefixPath) { + synchronized (this) { + return addCommonOverlayPathNative(themeApkPath, resApkPath, prefixPath); + } + } + + private native final int addCommonOverlayPathNative(String themeApkPath, + String resApkPath, String prefixPath); + + /** + * Add a set of assets as an icon pack. A pkgIdOverride value will change the package's id from + * what is in the resource table to a new value. Manage this carefully, if icon pack has more + * than one package then that next package's id will use pkgIdOverride+1. + * + * Icon packs are different from overlays as they have a different pkg id and + * do not use idmap so no targetPkg is required + * + * {@hide} + */ + public final int addIconPath(String idmapPath, String resApkPath, + String prefixPath, int pkgIdOverride) { + synchronized (this) { + return addIconPathNative(idmapPath, resApkPath, prefixPath, pkgIdOverride); + } + } + + private native final int addIconPathNative(String idmapPath, + String resApkPath, String prefixPath, int pkgIdOverride); + + /** + * Delete a set of overlay assets from the asset manager. Not for use by + * applications. Returns true if succeeded or false on failure. + * + * Also works for icon packs + * + * {@hide} + */ + public final boolean removeOverlayPath(String packageName, int cookie) { + synchronized (this) { + return removeOverlayPathNative(packageName, cookie); + } + } + + private native final boolean removeOverlayPathNative(String packageName, int cookie); /** * Add multiple sets of assets to the asset manager at once. See @@ -663,6 +738,126 @@ public final int[] addAssetPaths(String[] paths) { return cookies; } + /** + * Sets a flag indicating that this AssetManager should have themes + * attached, according to the initial request to create it by the + * ApplicationContext. + * + * {@hide} + */ + public final void setThemeSupport(boolean themeSupport) { + mThemeSupport = themeSupport; + } + + /** + * Should this AssetManager have themes attached, according to the initial + * request to create it by the ApplicationContext? + * + * {@hide} + */ + public final boolean hasThemeSupport() { + return mThemeSupport; + } + + /** + * Get package name of current icon pack (may return null). + * {@hide} + */ + public String getIconPackageName() { + return mIconPackageName; + } + + /** + * Sets icon package name + * {@hide} + */ + public void setIconPackageName(String packageName) { + mIconPackageName = packageName; + } + + /** + * Get package name of current common resources (may return null). + * {@hide} + */ + public String getCommonResPackageName() { + return mCommonResPackageName; + } + + /** + * Sets common resources package name + * {@hide} + */ + public void setCommonResPackageName(String packageName) { + mCommonResPackageName = packageName; + } + + /** + * Get package name of current theme (may return null). + * {@hide} + */ + public String getThemePackageName() { + return mThemePackageName; + } + + /** + * Sets package name and highest level style id for current theme (null, 0 is allowed). + * {@hide} + */ + public void setThemePackageName(String packageName) { + mThemePackageName = packageName; + } + + /** + * Get asset cookie for current theme (may return 0). + * {@hide} + */ + public ArrayList getThemeCookies() { + return mThemeCookies; + } + + /** {@hide} */ + public void setIconPackCookie(int cookie) { + mIconPackCookie = cookie; + } + + /** {@hide} */ + public int getIconPackCookie() { + return mIconPackCookie; + } + + /** {@hide} */ + public void setCommonResCookie(int cookie) { + mCommonResCookie = cookie; + } + + /** {@hide} */ + public int getCommonResCookie() { + return mCommonResCookie; + } + + /** + * Sets asset cookie for current theme (0 if not a themed asset manager). + * {@hide} + */ + public void addThemeCookie(int cookie) { + mThemeCookies.add(cookie); + } + + /** {@hide} */ + public String getAppName() { + return mAppName; + } + + /** {@hide} */ + public void setAppName(String pkgName) { + mAppName = pkgName; + } + + /** {@hide} */ + public boolean hasThemedAssets() { + return mThemeCookies.size() > 0; + } + /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to @@ -800,6 +995,26 @@ private native final int loadResourceBagValue(int ident, int bagEntryId, TypedVa /*package*/ native final int[] getStyleAttributes(int themeRes); private native final void init(boolean isSystem); + /** + * {@hide} + */ + public native final int getBasePackageCount(); + + /** + * {@hide} + */ + public native final String getBasePackageName(int index); + + /** + * {@hide} + */ + public native final String getBaseResourcePackageName(int index); + + /** + * {@hide} + */ + public native final int getBasePackageId(int index); + private native final void destroy(); private final void incRefsLocked(long id) { diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index da35ee92267e4..2b8951e0f8cda 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -241,8 +241,7 @@ public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, mCompatibilityFlags = compatFlags; } - private CompatibilityInfo(int compFlags, - int dens, float scale, float invertedScale) { + private CompatibilityInfo(int compFlags, int dens, float scale, float invertedScale) { mCompatibilityFlags = compFlags; applicationDensity = dens; applicationScale = scale; diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index fd6047684eb51..0bc1ec22a34eb 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,6 +82,11 @@ public final class Configuration implements Parcelable, Comparable + + diff --git a/core/res/res/anim/last_app_in.xml b/core/res/res/anim/last_app_in.xml new file mode 100644 index 0000000000000..bf89dbf922f2b --- /dev/null +++ b/core/res/res/anim/last_app_in.xml @@ -0,0 +1,41 @@ + + + + + + + + + + diff --git a/core/res/res/anim/last_app_out.xml b/core/res/res/anim/last_app_out.xml new file mode 100644 index 0000000000000..834dfff5b1a76 --- /dev/null +++ b/core/res/res/anim/last_app_out.xml @@ -0,0 +1,41 @@ + + + + + + + + + + diff --git a/core/res/res/anim/lock_screen_wallpaper_exit_noop.xml b/core/res/res/anim/lock_screen_wallpaper_exit_noop.xml new file mode 100644 index 0000000000000..4cc5c70aae3bf --- /dev/null +++ b/core/res/res/anim/lock_screen_wallpaper_exit_noop.xml @@ -0,0 +1,32 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/res/res/color/preference_category_text_color.xml b/core/res/res/color/preference_category_text_color.xml new file mode 100644 index 0000000000000..7916a5ccb2578 --- /dev/null +++ b/core/res/res/color/preference_category_text_color.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/core/res/res/drawable-hdpi/ic_launcher_android.png b/core/res/res/drawable-hdpi/ic_launcher_android.png index cce518787b582..cde69bcccec65 100644 Binary files a/core/res/res/drawable-hdpi/ic_launcher_android.png and b/core/res/res/drawable-hdpi/ic_launcher_android.png differ diff --git a/core/res/res/drawable-hdpi/ic_lock_power_reboot_alpha.png b/core/res/res/drawable-hdpi/ic_lock_power_reboot_alpha.png new file mode 100644 index 0000000000000..ca00936e5406a Binary files /dev/null and b/core/res/res/drawable-hdpi/ic_lock_power_reboot_alpha.png differ diff --git a/core/res/res/drawable-hdpi/perm_group_security.png b/core/res/res/drawable-hdpi/perm_group_security.png new file mode 100644 index 0000000000000..47f0d6fd6fd6a Binary files /dev/null and b/core/res/res/drawable-hdpi/perm_group_security.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png index 39d2c95ffab21..3a9031e8e0049 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png and b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png index 3a9031e8e0049..39d2c95ffab21 100644 Binary files a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png and b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png differ diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png index 6a97d5b79a283..c133a0cbd379f 100644 Binary files a/core/res/res/drawable-mdpi/ic_launcher_android.png and b/core/res/res/drawable-mdpi/ic_launcher_android.png differ diff --git a/core/res/res/drawable-mdpi/ic_lock_power_reboot_alpha.png b/core/res/res/drawable-mdpi/ic_lock_power_reboot_alpha.png new file mode 100644 index 0000000000000..2b125b98f4cba Binary files /dev/null and b/core/res/res/drawable-mdpi/ic_lock_power_reboot_alpha.png differ diff --git a/core/res/res/drawable-mdpi/perm_group_security.png b/core/res/res/drawable-mdpi/perm_group_security.png new file mode 100644 index 0000000000000..cc00c1682125c Binary files /dev/null and b/core/res/res/drawable-mdpi/perm_group_security.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png index b9c364c92b156..217ea4eb65792 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png and b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png index 217ea4eb65792..b9c364c92b156 100644 Binary files a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png and b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png differ diff --git a/core/res/res/drawable-nodpi/platlogo_cm.xml b/core/res/res/drawable-nodpi/platlogo_cm.xml new file mode 100644 index 0000000000000..b863c271974a9 --- /dev/null +++ b/core/res/res/drawable-nodpi/platlogo_cm.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml index 8cc9961bf6e62..9dd94971b450b 100644 --- a/core/res/res/drawable-nodpi/stat_sys_adb.xml +++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml @@ -1,11 +1,11 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + + android:fillColor="#BBFFFFFF" + android:pathData="M21.4,6.9c0,1-4.2,1.9-9.4,1.9S2.6,7.9,2.6,6.9S6.8,5,12,5S21.4,5.8,21.4,6.9z" /> + android:fillColor="#FFFFFF" + android:pathData="M19.7,6.5c-0.1,0.3-0.5,0.5-0.8,0.3c-0.3-0.1-0.5-0.5-0.3-0.8l1.3-3.1c0.1-0.3,0.5-0.5,0.8-0.3 +C20.9,2.7,21.1,3,21,3.4L19.7,6.5z" /> - + android:fillColor="#FFFFFF" + android:pathData="M4.3,6.5C4.5,6.8,4.8,7,5.1,6.8C5.5,6.7,5.6,6.3,5.5,6L4.2,2.9C4.1,2.6,3.7,2.4,3.4,2.5 +C3.1,2.7,2.9,3,3,3.4L4.3,6.5z" /> diff --git a/core/res/res/drawable-xhdpi/ic_launcher_android.png b/core/res/res/drawable-xhdpi/ic_launcher_android.png index b1097d6a6279b..bfa42f0e7b91d 100644 Binary files a/core/res/res/drawable-xhdpi/ic_launcher_android.png and b/core/res/res/drawable-xhdpi/ic_launcher_android.png differ diff --git a/core/res/res/drawable-xhdpi/ic_lock_power_reboot_alpha.png b/core/res/res/drawable-xhdpi/ic_lock_power_reboot_alpha.png new file mode 100644 index 0000000000000..653970f226006 Binary files /dev/null and b/core/res/res/drawable-xhdpi/ic_lock_power_reboot_alpha.png differ diff --git a/core/res/res/drawable-xhdpi/perm_group_security.png b/core/res/res/drawable-xhdpi/perm_group_security.png new file mode 100644 index 0000000000000..d165c74c4575b Binary files /dev/null and b/core/res/res/drawable-xhdpi/perm_group_security.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png index cd0ca73608da7..e443f4573f1ae 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim1.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png index e443f4573f1ae..cd0ca73608da7 100644 Binary files a/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png and b/core/res/res/drawable-xhdpi/stat_sys_upload_anim2.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_launcher_android.png b/core/res/res/drawable-xxhdpi/ic_launcher_android.png new file mode 100644 index 0000000000000..324e72cdd7480 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/ic_launcher_android.png differ diff --git a/core/res/res/drawable-xxhdpi/ic_lock_power_reboot_alpha.png b/core/res/res/drawable-xxhdpi/ic_lock_power_reboot_alpha.png new file mode 100644 index 0000000000000..362bf282f2df8 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/ic_lock_power_reboot_alpha.png differ diff --git a/core/res/res/drawable-xxhdpi/perm_group_security.png b/core/res/res/drawable-xxhdpi/perm_group_security.png new file mode 100644 index 0000000000000..670ae684740d8 Binary files /dev/null and b/core/res/res/drawable-xxhdpi/perm_group_security.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png index 39dd3b800cb5d..b828430b766a4 100755 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim1.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png index b828430b766a4..39dd3b800cb5d 100755 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png and b/core/res/res/drawable-xxhdpi/stat_sys_upload_anim2.png differ diff --git a/core/res/res/drawable-xxxhdpi/ic_launcher_android.png b/core/res/res/drawable-xxxhdpi/ic_launcher_android.png new file mode 100644 index 0000000000000..aee44e1384346 Binary files /dev/null and b/core/res/res/drawable-xxxhdpi/ic_launcher_android.png differ diff --git a/core/res/res/drawable/ic_lock_dnd_priority.xml b/core/res/res/drawable/ic_lock_dnd_priority.xml new file mode 100644 index 0000000000000..0d455522be205 --- /dev/null +++ b/core/res/res/drawable/ic_lock_dnd_priority.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_lock_dnd_total_silence.xml b/core/res/res/drawable/ic_lock_dnd_total_silence.xml new file mode 100644 index 0000000000000..0128dfe7e0d25 --- /dev/null +++ b/core/res/res/drawable/ic_lock_dnd_total_silence.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_lock_lock.xml b/core/res/res/drawable/ic_lock_lock.xml index 39f268a9d6ae3..6c87b42915593 100644 --- a/core/res/res/drawable/ic_lock_lock.xml +++ b/core/res/res/drawable/ic_lock_lock.xml @@ -16,4 +16,4 @@ + android:tint="?attr/colorControlNormal" /> diff --git a/core/res/res/drawable/ic_lock_power_reboot.xml b/core/res/res/drawable/ic_lock_power_reboot.xml new file mode 100644 index 0000000000000..82c79270130fd --- /dev/null +++ b/core/res/res/drawable/ic_lock_power_reboot.xml @@ -0,0 +1,33 @@ + + + + + diff --git a/core/res/res/drawable/ic_lock_ring.xml b/core/res/res/drawable/ic_lock_ring.xml new file mode 100644 index 0000000000000..92b4b2e53d902 --- /dev/null +++ b/core/res/res/drawable/ic_lock_ring.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_lock_screenshot.xml b/core/res/res/drawable/ic_lock_screenshot.xml new file mode 100644 index 0000000000000..b682f920319c0 --- /dev/null +++ b/core/res/res/drawable/ic_lock_screenshot.xml @@ -0,0 +1,27 @@ + + + + + + diff --git a/core/res/res/drawable/ic_lock_settings.xml b/core/res/res/drawable/ic_lock_settings.xml new file mode 100644 index 0000000000000..4c6ded2a5f032 --- /dev/null +++ b/core/res/res/drawable/ic_lock_settings.xml @@ -0,0 +1,27 @@ + + + + + + diff --git a/core/res/res/drawable/ic_lock_user.xml b/core/res/res/drawable/ic_lock_user.xml new file mode 100644 index 0000000000000..e951319cb2291 --- /dev/null +++ b/core/res/res/drawable/ic_lock_user.xml @@ -0,0 +1,27 @@ + + + + + + diff --git a/core/res/res/drawable/ic_lock_vibrate.xml b/core/res/res/drawable/ic_lock_vibrate.xml new file mode 100644 index 0000000000000..f38ebc29774b7 --- /dev/null +++ b/core/res/res/drawable/ic_lock_vibrate.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_power_dnd_priority.xml b/core/res/res/drawable/ic_power_dnd_priority.xml new file mode 100644 index 0000000000000..87a12165fcbd2 --- /dev/null +++ b/core/res/res/drawable/ic_power_dnd_priority.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/core/res/res/drawable/ic_power_dnd_total_silence.xml b/core/res/res/drawable/ic_power_dnd_total_silence.xml new file mode 100644 index 0000000000000..bd4e2ed8477ce --- /dev/null +++ b/core/res/res/drawable/ic_power_dnd_total_silence.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/core/res/res/drawable/ic_power_ring.xml b/core/res/res/drawable/ic_power_ring.xml new file mode 100644 index 0000000000000..3f3b856a5b97b --- /dev/null +++ b/core/res/res/drawable/ic_power_ring.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/core/res/res/drawable/ic_power_vibrate.xml b/core/res/res/drawable/ic_power_vibrate.xml new file mode 100644 index 0000000000000..068286b0b8c0d --- /dev/null +++ b/core/res/res/drawable/ic_power_vibrate.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml index 4f016bcc2e83e..44b410393182f 100644 --- a/core/res/res/drawable/progress_large.xml +++ b/core/res/res/drawable/progress_large.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_76" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_large_white.xml b/core/res/res/drawable/progress_large_white.xml index c690ed4e0e9ae..6c2388c680a3f 100644 --- a/core/res/res/drawable/progress_large_white.xml +++ b/core/res/res/drawable/progress_large_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_76" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml index eb1bd50d17d73..82ad686b67390 100644 --- a/core/res/res/drawable/progress_medium.xml +++ b/core/res/res/drawable/progress_medium.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_48" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_medium_white.xml b/core/res/res/drawable/progress_medium_white.xml index b4f9b318a902a..886ee05bc9c78 100644 --- a/core/res/res/drawable/progress_medium_white.xml +++ b/core/res/res/drawable/progress_medium_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_48" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml index e0ee5e47d8305..1881da3848924 100644 --- a/core/res/res/drawable/progress_small.xml +++ b/core/res/res/drawable/progress_small.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml index 8cfba864b5b2c..5bb5cf8749dc4 100644 --- a/core/res/res/drawable/progress_small_titlebar.xml +++ b/core/res/res/drawable/progress_small_titlebar.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_small_white.xml b/core/res/res/drawable/progress_small_white.xml index 8cfba864b5b2c..5bb5cf8749dc4 100644 --- a/core/res/res/drawable/progress_small_white.xml +++ b/core/res/res/drawable/progress_small_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/search_spinner.xml b/core/res/res/drawable/search_spinner.xml index 31a77c30cf2a6..b8c8b09fa882e 100644 --- a/core/res/res/drawable/search_spinner.xml +++ b/core/res/res/drawable/search_spinner.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_20" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/stat_notify_privacy_guard.xml b/core/res/res/drawable/stat_notify_privacy_guard.xml new file mode 100644 index 0000000000000..bcf3a7f31809c --- /dev/null +++ b/core/res/res/drawable/stat_notify_privacy_guard.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/core/res/res/drawable/stat_notify_protected.xml b/core/res/res/drawable/stat_notify_protected.xml new file mode 100644 index 0000000000000..d67a348cf5ec8 --- /dev/null +++ b/core/res/res/drawable/stat_notify_protected.xml @@ -0,0 +1,28 @@ + + + + + + + \ No newline at end of file diff --git a/core/res/res/layout-xlarge/activity_list.xml b/core/res/res/layout-xlarge/activity_list.xml index bf46af8f4f37c..abd9b643e3540 100644 --- a/core/res/res/layout-xlarge/activity_list.xml +++ b/core/res/res/layout-xlarge/activity_list.xml @@ -58,7 +58,7 @@ + android:background="@color/activity_list_divider_color" /> diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml index 34cb21d4f7f5f..44a11d9d5b8c6 100644 --- a/core/res/res/layout/alert_dialog_holo.xml +++ b/core/res/res/layout/alert_dialog_holo.xml @@ -34,7 +34,7 @@ android:layout_width="match_parent" android:layout_height="2dip" android:visibility="gone" - android:background="@android:color/holo_blue_light" /> + android:background="@color/dialog_divider_color" /> + android:background="@color/dialog_divider_color" /> diff --git a/core/res/res/layout/app_permission_item.xml b/core/res/res/layout/app_permission_item.xml index 383d771074e09..a80d40efd2bee 100644 --- a/core/res/res/layout/app_permission_item.xml +++ b/core/res/res/layout/app_permission_item.xml @@ -32,7 +32,7 @@ android:layout_marginStart="16dp" android:layout_marginEnd="8dp" android:scaleType="fitCenter" - android:tint="@android:color/black"/> + android:tint="@color/app_permission_icon_tint"/> diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 41726fb71f7f2..c9690603261e5 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -31,7 +31,7 @@ android:layout_alwaysShow="true" android:elevation="8dp" android:paddingStart="16dp" - android:background="@color/white" > + android:background="@color/resolver_list_bg" > + android:background="@color/dialog_divider_color" /> + android:background="@color/dialog_divider_color" /> + android:background="@color/dialog_divider_color" /> + + + + + + + + @@ -94,10 +124,10 @@ android:layout_marginEnd="8dp" android:layout_marginTop="6dp" android:layout_marginBottom="6dp" - android:src="@drawable/ic_audio_ring_notif" + android:src="@drawable/ic_power_ring" android:scaleType="center" android:duplicateParentState="true" android:background="@drawable/silent_mode_indicator" /> - \ No newline at end of file + diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml index 28fbea577aa55..9f3d44d1ba225 100644 --- a/core/res/res/layout/immersive_mode_cling.xml +++ b/core/res/res/layout/immersive_mode_cling.xml @@ -16,7 +16,7 @@ @@ -47,7 +47,7 @@ android:paddingTop="8dp" android:scaleType="center" android:src="@drawable/ic_expand_more_48dp" - android:tint="#ff009688"/> + android:tint="@color/immersive_cling_bg_color"/>