diff options
501 files changed, 15564 insertions, 8529 deletions
@@ -118,6 +118,7 @@ LOCAL_SRC_FILES += \ core/java/android/content/IIntentReceiver.aidl \ core/java/android/content/IIntentSender.aidl \ core/java/android/content/IOnPrimaryClipChangedListener.aidl \ + core/java/android/content/IRestrictionsManager.aidl \ core/java/android/content/ISyncAdapter.aidl \ core/java/android/content/ISyncContext.aidl \ core/java/android/content/ISyncServiceAdapter.aidl \ @@ -381,54 +382,14 @@ LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_STATIC_JAVA_LIBRARIES := framework-base LOCAL_DX_FLAGS := --core-library -# Packages to include, use \* wildcard to include descendants. +# List of packages to include along with their descendants. LOCAL_JAR_PACKAGES := \ - android \ - android.accessibilityservice\* \ - android.accounts\* \ - android.alsa\* \ - android.animation\* \ - android.annotation\* \ - android.app\* \ - android.appwidget\* \ - android.bluetooth\* \ - android.content\* \ - android.content\* \ - android.database\* \ - android.ddm\* \ - android.drm\* \ - android.emoji\* \ - android.filterfw\* \ - android.filterpacks\* \ - android.gesture\* \ - android.graphics\* \ - android.inputmethodservice\* \ - android.location\* \ - android.media\* \ - android.mtp\* \ - android.net\* \ - android.nfc\* \ - android.opengl\* \ - android.os\* \ - android.preference\* \ - android.print\* \ - android.printservice\* \ - android.provider\* \ - android.renderscript\* \ - android.sax\* \ - android.security\* \ - android.service\* \ - android.speech\* \ - android.system\* \ - android.telecomm\* \ - android.telephony\* \ - android.test\* \ - android.text\* \ - android.transition\* \ - android.util\* \ - android.view\* \ - android.webkit\* \ - android.widget\* + android + +# List of packages to exclude along with their descendants. +# Overrides inclusion. +LOCAL_JAR_EXCLUDE_PACKAGES := \ + android.hardware # List of classes and interfaces which should be loaded by the Zygote. LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes @@ -446,16 +407,11 @@ LOCAL_NO_STANDARD_LIBRARIES := true LOCAL_STATIC_JAVA_LIBRARIES := framework-base LOCAL_DX_FLAGS := --core-library -# Packages to include, use \* wildcard to include descendants. -# 'android' is required to be able to include 'android.hardware', -# however, it causes classes in 'android' to be part of framework -# and framework2. -# TODO: Finer grained matching. +# List of packages to include along with their descendants. LOCAL_JAR_PACKAGES := \ - android \ - android.hardware\* \ - com\* \ - javax\* + android.hardware \ + com \ + javax include $(BUILD_JAVA_LIBRARY) framework2_module := $(LOCAL_INSTALLED_MODULE) diff --git a/api/current.txt b/api/current.txt index 1997421..b5849ed 100644 --- a/api/current.txt +++ b/api/current.txt @@ -749,6 +749,7 @@ package android { field public static final int layout_centerVertical = 16843153; // 0x1010191 field public static final int layout_column = 16843084; // 0x101014c field public static final int layout_columnSpan = 16843645; // 0x101037d + field public static final int layout_columnWeight = 16843868; // 0x101045c field public static final int layout_gravity = 16842931; // 0x10100b3 field public static final int layout_height = 16842997; // 0x10100f5 field public static final int layout_margin = 16842998; // 0x10100f6 @@ -760,6 +761,7 @@ package android { field public static final int layout_marginTop = 16843000; // 0x10100f8 field public static final int layout_row = 16843643; // 0x101037b field public static final int layout_rowSpan = 16843644; // 0x101037c + field public static final int layout_rowWeight = 16843867; // 0x101045b field public static final int layout_scale = 16843155; // 0x1010193 field public static final int layout_span = 16843085; // 0x101014d field public static final int layout_toEndOf = 16843704; // 0x10103b8 @@ -1015,7 +1017,7 @@ package android { field public static final int selectAllOnFocus = 16843102; // 0x101015e field public static final int selectable = 16843238; // 0x10101e6 field public static final int selectableItemBackground = 16843534; // 0x101030e - field public static final int selectableItemBackgroundBorderless = 16843867; // 0x101045b + field public static final int selectableItemBackgroundBorderless = 16843871; // 0x101045f field public static final int selectedDateVerticalBar = 16843591; // 0x1010347 field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342 field public static final int sessionService = 16843841; // 0x1010441 @@ -1242,6 +1244,8 @@ package android { field public static final int transition = 16843743; // 0x10103df field public static final int transitionGroup = 16843803; // 0x101041b field public static final int transitionOrdering = 16843744; // 0x10103e0 + field public static final int translateX = 16843869; // 0x101045d + field public static final int translateY = 16843870; // 0x101045e field public static final int translationX = 16843554; // 0x1010322 field public static final int translationY = 16843555; // 0x1010323 field public static final int translationZ = 16843796; // 0x1010414 @@ -5163,10 +5167,10 @@ package android.app.admin { } public class DevicePolicyManager { - method public void addForwardingIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); + method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int); method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName); method public void addUserRestriction(android.content.ComponentName, java.lang.String); - method public void clearForwardingIntentFilters(android.content.ComponentName); + method public void clearCrossProfileIntentFilters(android.content.ComponentName); method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String); method public void clearUserRestriction(android.content.ComponentName, java.lang.String); method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String); @@ -5226,6 +5230,7 @@ package android.app.admin { method public void setPasswordMinimumUpperCase(android.content.ComponentName, int); method public void setPasswordQuality(android.content.ComponentName, int); method public void setProfileEnabled(android.content.ComponentName); + method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName); method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public int setStorageEncryption(android.content.ComponentName, boolean); method public void wipeData(int); @@ -5241,8 +5246,9 @@ package android.app.admin { field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN"; field public static final java.lang.String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = "defaultManagedProfileName"; field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "deviceAdminPackageName"; - field public static int FLAG_TO_MANAGED_PROFILE; - field public static int FLAG_TO_PRIMARY_USER; + field public static final java.lang.String EXTRA_PROVISIONING_TOKEN = "android.app.extra.token"; + field public static int FLAG_MANAGED_CAN_ACCESS_PARENT; + field public static int FLAG_PARENT_CAN_ACCESS_MANAGED; field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0 field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2 @@ -7021,6 +7027,7 @@ package android.content { field public static final java.lang.String NSD_SERVICE = "servicediscovery"; field public static final java.lang.String POWER_SERVICE = "power"; field public static final java.lang.String PRINT_SERVICE = "print"; + field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions"; field public static final java.lang.String SEARCH_SERVICE = "search"; field public static final java.lang.String SENSOR_SERVICE = "sensor"; field public static final java.lang.String STORAGE_SERVICE = "storage"; @@ -7777,12 +7784,14 @@ package android.content { ctor public RestrictionEntry(java.lang.String, java.lang.String); ctor public RestrictionEntry(java.lang.String, boolean); ctor public RestrictionEntry(java.lang.String, java.lang.String[]); + ctor public RestrictionEntry(java.lang.String, int); ctor public RestrictionEntry(android.os.Parcel); method public int describeContents(); method public java.lang.String[] getAllSelectedStrings(); method public java.lang.String[] getChoiceEntries(); method public java.lang.String[] getChoiceValues(); method public java.lang.String getDescription(); + method public int getIntValue(); method public java.lang.String getKey(); method public boolean getSelectedState(); method public java.lang.String getSelectedString(); @@ -7794,6 +7803,7 @@ package android.content { method public void setChoiceValues(java.lang.String[]); method public void setChoiceValues(android.content.Context, int); method public void setDescription(java.lang.String); + method public void setIntValue(int); method public void setSelectedState(boolean); method public void setSelectedString(java.lang.String); method public void setTitle(java.lang.String); @@ -7802,10 +7812,36 @@ package android.content { field public static final android.os.Parcelable.Creator CREATOR; field public static final int TYPE_BOOLEAN = 1; // 0x1 field public static final int TYPE_CHOICE = 2; // 0x2 + field public static final int TYPE_INTEGER = 5; // 0x5 field public static final int TYPE_MULTI_SELECT = 4; // 0x4 field public static final int TYPE_NULL = 0; // 0x0 } + public class RestrictionsManager { + method public android.os.Bundle getApplicationRestrictions(); + method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String); + method public boolean hasRestrictionsProvider(); + method public void notifyPermissionResponse(java.lang.String, android.os.Bundle); + method public void requestPermission(java.lang.String, android.os.Bundle); + field public static final java.lang.String ACTION_PERMISSION_RESPONSE_RECEIVED = "android.intent.action.PERMISSION_RESPONSE_RECEIVED"; + field public static final java.lang.String ACTION_REQUEST_PERMISSION = "android.intent.action.REQUEST_PERMISSION"; + field public static final java.lang.String EXTRA_PACKAGE_NAME = "package_name"; + field public static final java.lang.String EXTRA_REQUEST_BUNDLE = "request_bundle"; + field public static final java.lang.String EXTRA_RESPONSE_BUNDLE = "response_bundle"; + field public static final java.lang.String EXTRA_TEMPLATE_ID = "template_id"; + field public static final java.lang.String REQUEST_KEY_APPROVE_LABEL = "android.req_template.accept"; + field public static final java.lang.String REQUEST_KEY_DATA = "android.req_template.data"; + field public static final java.lang.String REQUEST_KEY_DENY_LABEL = "android.req_template.reject"; + field public static final java.lang.String REQUEST_KEY_DEVICE_NAME = "android.req_template.device"; + field public static final java.lang.String REQUEST_KEY_ICON = "android.req_template.icon"; + field public static final java.lang.String REQUEST_KEY_ID = "android.req_template.req_id"; + field public static final java.lang.String REQUEST_KEY_MESSAGE = "android.req_template.mesg"; + field public static final java.lang.String REQUEST_KEY_REQUESTOR_NAME = "android.req_template.requestor"; + field public static final java.lang.String REQUEST_KEY_TITLE = "android.req_template.title"; + field public static final java.lang.String REQUEST_TEMPLATE_QUESTION = "android.req_template.type.simple"; + field public static final java.lang.String RESPONSE_KEY_BOOLEAN = "android.req_template.response"; + } + public class SearchRecentSuggestionsProvider extends android.content.ContentProvider { ctor public SearchRecentSuggestionsProvider(); method public int delete(android.net.Uri, java.lang.String, java.lang.String[]); @@ -10170,8 +10206,8 @@ package android.graphics { method public boolean clipRect(float, float, float, float, android.graphics.Region.Op); method public boolean clipRect(float, float, float, float); method public boolean clipRect(int, int, int, int); - method public boolean clipRegion(android.graphics.Region, android.graphics.Region.Op); - method public boolean clipRegion(android.graphics.Region); + method public deprecated boolean clipRegion(android.graphics.Region, android.graphics.Region.Op); + method public deprecated boolean clipRegion(android.graphics.Region); method public void concat(android.graphics.Matrix); method public void drawARGB(int, int, int, int); method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint); @@ -11552,6 +11588,16 @@ package android.graphics.drawable { method public void startTransition(int); } + public class VectorDrawable extends android.graphics.drawable.Drawable { + ctor public VectorDrawable(); + method public void draw(android.graphics.Canvas); + method public int getOpacity(); + method public void setAlpha(int); + method public void setColorFilter(android.graphics.ColorFilter); + method public void setPadding(android.graphics.Rect); + method public void setPadding(int, int, int, int); + } + } package android.graphics.drawable.shapes { @@ -11922,16 +11968,19 @@ package android.hardware { public final class Sensor { method public int getFifoMaxEventCount(); method public int getFifoReservedEventCount(); + method public int getMaxDelay(); method public float getMaximumRange(); method public int getMinDelay(); method public java.lang.String getName(); method public float getPower(); - method public java.lang.String getRequiredPermission(); method public float getResolution(); method public java.lang.String getStringType(); method public int getType(); method public java.lang.String getVendor(); method public int getVersion(); + method public boolean isWakeUpSensor(); + field public static final java.lang.String SENSOR_STRING_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = "android.sensor.non_wake_up_proximity_sensor"; + field public static final java.lang.String SENSOR_STRING_TYPE_WAKE_UP_TILT_DETECTOR = "android.sensor.wake_up_tilt_detector"; field public static final java.lang.String STRING_TYPE_ACCELEROMETER = "android.sensor.accelerometer"; field public static final java.lang.String STRING_TYPE_AMBIENT_TEMPERATURE = "android.sensor.ambient_temperature"; field public static final java.lang.String STRING_TYPE_GAME_ROTATION_VECTOR = "android.sensor.game_rotation_vector"; @@ -11953,6 +12002,24 @@ package android.hardware { field public static final java.lang.String STRING_TYPE_STEP_COUNTER = "android.sensor.step_counter"; field public static final java.lang.String STRING_TYPE_STEP_DETECTOR = "android.sensor.step_detector"; field public static final deprecated java.lang.String STRING_TYPE_TEMPERATURE = "android.sensor.temperature"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_ACCELEROMETER = "android.sensor.wake_up_accelerometer"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_AMBIENT_TEMPERATURE = "android.sensor.wake_up_ambient_temperature"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_GAME_ROTATION_VECTOR = "android.sensor.wake_up_game_rotation_vector"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = "android.sensor.wake_up_geomagnetic_rotation_vector"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_GRAVITY = "android.sensor.wake_up_gravity"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_GYROSCOPE = "android.sensor.wake_up_gyroscope"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = "android.sensor.wake_up_gyroscope_uncalibrated"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_HEART_RATE = "android.sensor.wake_up_heart_rate"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_LIGHT = "android.sensor.wake_up_light"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_LINEAR_ACCELERATION = "android.sensor.wake_up_linear_acceleration"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD = "android.sensor.wake_up_magnetic_field"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = "android.sensor.wake_up_magnetic_field_uncalibrated"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_ORIENTATION = "android.sensor.wake_up_orientation"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_PRESSURE = "android.sensor.wake_up_pressure"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_RELATIVE_HUMIDITY = "android.sensor.wake_up_relative_humidity"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_ROTATION_VECTOR = "android.sensor.wake_up_rotation_vector"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_STEP_COUNTER = "android.sensor.wake_up_step_counter"; + field public static final java.lang.String STRING_TYPE_WAKE_UP_STEP_DETECTOR = "android.sensor.wake_up_step_detector"; field public static final int TYPE_ACCELEROMETER = 1; // 0x1 field public static final int TYPE_ALL = -1; // 0xffffffff field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd @@ -11966,6 +12033,7 @@ package android.hardware { field public static final int TYPE_LINEAR_ACCELERATION = 10; // 0xa field public static final int TYPE_MAGNETIC_FIELD = 2; // 0x2 field public static final int TYPE_MAGNETIC_FIELD_UNCALIBRATED = 14; // 0xe + field public static final int TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = 22; // 0x16 field public static final deprecated int TYPE_ORIENTATION = 3; // 0x3 field public static final int TYPE_PRESSURE = 6; // 0x6 field public static final int TYPE_PROXIMITY = 8; // 0x8 @@ -11975,6 +12043,25 @@ package android.hardware { field public static final int TYPE_STEP_COUNTER = 19; // 0x13 field public static final int TYPE_STEP_DETECTOR = 18; // 0x12 field public static final deprecated int TYPE_TEMPERATURE = 7; // 0x7 + field public static final int TYPE_WAKE_UP_ACCELEROMETER = 23; // 0x17 + field public static final int TYPE_WAKE_UP_AMBIENT_TEMPERATURE = 33; // 0x21 + field public static final int TYPE_WAKE_UP_GAME_ROTATION_VECTOR = 35; // 0x23 + field public static final int TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = 39; // 0x27 + field public static final int TYPE_WAKE_UP_GRAVITY = 29; // 0x1d + field public static final int TYPE_WAKE_UP_GYROSCOPE = 26; // 0x1a + field public static final int TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = 36; // 0x24 + field public static final int TYPE_WAKE_UP_HEART_RATE = 40; // 0x28 + field public static final int TYPE_WAKE_UP_LIGHT = 27; // 0x1b + field public static final int TYPE_WAKE_UP_LINEAR_ACCELERATION = 30; // 0x1e + field public static final int TYPE_WAKE_UP_MAGNETIC_FIELD = 24; // 0x18 + field public static final int TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = 34; // 0x22 + field public static final int TYPE_WAKE_UP_ORIENTATION = 25; // 0x19 + field public static final int TYPE_WAKE_UP_PRESSURE = 28; // 0x1c + field public static final int TYPE_WAKE_UP_RELATIVE_HUMIDITY = 32; // 0x20 + field public static final int TYPE_WAKE_UP_ROTATION_VECTOR = 31; // 0x1f + field public static final int TYPE_WAKE_UP_STEP_COUNTER = 38; // 0x26 + field public static final int TYPE_WAKE_UP_STEP_DETECTOR = 37; // 0x25 + field public static final int TYPE_WAKE_UP_TILT_DETECTOR = 41; // 0x29 } public class SensorEvent { @@ -12076,6 +12163,7 @@ package android.hardware { field public static final int SENSOR_STATUS_ACCURACY_HIGH = 3; // 0x3 field public static final int SENSOR_STATUS_ACCURACY_LOW = 1; // 0x1 field public static final int SENSOR_STATUS_ACCURACY_MEDIUM = 2; // 0x2 + field public static final int SENSOR_STATUS_NO_CONTACT = -1; // 0xffffffff field public static final int SENSOR_STATUS_UNRELIABLE = 0; // 0x0 field public static final deprecated int SENSOR_TEMPERATURE = 4; // 0x4 field public static final deprecated int SENSOR_TRICORDER = 64; // 0x40 @@ -13720,8 +13808,46 @@ package android.media { method public void stop(); } + public final class AudioAttributes { + method public int getContentType(); + method public int getFlags(); + method public java.util.Set<java.lang.String> getTags(); + method public int getUsage(); + field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 + field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2 + field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4 + field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1 + field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0 + field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1 + field public static final int USAGE_ALARM = 4; // 0x4 + field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb + field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc + field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd + field public static final int USAGE_GAME = 14; // 0xe + field public static final int USAGE_MEDIA = 1; // 0x1 + field public static final int USAGE_NOTIFICATION = 5; // 0x5 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7 + field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa + field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6 + field public static final int USAGE_UNKNOWN = 0; // 0x0 + field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2 + field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3 + } + + public static class AudioAttributes.Builder { + ctor public AudioAttributes.Builder(); + ctor public AudioAttributes.Builder(android.media.AudioAttributes); + method public android.media.AudioAttributes.Builder addTag(java.lang.String); + method public android.media.AudioAttributes build(); + method public android.media.AudioAttributes.Builder setContentType(int); + method public android.media.AudioAttributes.Builder setFlags(int); + method public android.media.AudioAttributes.Builder setLegacyStreamType(int); + method public android.media.AudioAttributes.Builder setUsage(int); + } + public class AudioFormat { - ctor public AudioFormat(); field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1 field public static final deprecated int CHANNEL_CONFIGURATION_INVALID = 0; // 0x0 field public static final deprecated int CHANNEL_CONFIGURATION_MONO = 2; // 0x2 @@ -17202,11 +17328,9 @@ package android.net.wifi { } public static final class WifiEnterpriseConfig.Eap { - field public static final int AKA = 5; // 0x5 field public static final int NONE = -1; // 0xffffffff field public static final int PEAP = 0; // 0x0 field public static final int PWD = 3; // 0x3 - field public static final int SIM = 4; // 0x4 field public static final int TLS = 1; // 0x1 field public static final int TTLS = 2; // 0x2 } @@ -17240,6 +17364,7 @@ package android.net.wifi { public class WifiManager { method public int addNetwork(android.net.wifi.WifiConfiguration); method public static int calculateSignalLevel(int, int); + method public void cancelWps(android.net.wifi.WifiManager.ActionListener); method public static int compareSignalLevel(int, int); method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String); method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String); @@ -17263,9 +17388,12 @@ package android.net.wifi { method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); method public boolean setWifiEnabled(boolean); method public boolean startScan(); + method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsListener); method public int updateNetwork(android.net.wifi.WifiConfiguration); field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; + field public static final int BUSY = 2; // 0x2 + field public static final int ERROR = 0; // 0x0 field public static final int ERROR_AUTHENTICATING = 1; // 0x1 field public static final java.lang.String EXTRA_BSSID = "bssid"; field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; @@ -17276,6 +17404,8 @@ package android.net.wifi { field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError"; field public static final java.lang.String EXTRA_WIFI_INFO = "wifiInfo"; field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_state"; + field public static final int INVALID_ARGS = 8; // 0x8 + field public static final int IN_PROGRESS = 1; // 0x1 field public static final java.lang.String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED"; field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE"; field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED"; @@ -17291,6 +17421,16 @@ package android.net.wifi { field public static final int WIFI_STATE_ENABLED = 3; // 0x3 field public static final int WIFI_STATE_ENABLING = 2; // 0x2 field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4 + field public static final int WPS_AUTH_FAILURE = 6; // 0x6 + field public static final int WPS_OVERLAP_ERROR = 3; // 0x3 + field public static final int WPS_TIMED_OUT = 7; // 0x7 + field public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5 + field public static final int WPS_WEP_PROHIBITED = 4; // 0x4 + } + + public static abstract interface WifiManager.ActionListener { + method public abstract void onFailure(int); + method public abstract void onSuccess(); } public class WifiManager.MulticastLock { @@ -17308,11 +17448,18 @@ package android.net.wifi { method public void setWorkSource(android.os.WorkSource); } + public static abstract interface WifiManager.WpsListener { + method public abstract void onCompletion(); + method public abstract void onFailure(int); + method public abstract void onStartSuccess(java.lang.String); + } + public class WpsInfo implements android.os.Parcelable { ctor public WpsInfo(); ctor public WpsInfo(android.net.wifi.WpsInfo); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); + field public java.lang.String BSSID; field public static final android.os.Parcelable.Creator CREATOR; field public static final int DISPLAY = 1; // 0x1 field public static final int INVALID = 4; // 0x4 @@ -21593,7 +21740,9 @@ package android.os { method public abstract void cancel(); method public abstract boolean hasVibrator(); method public void vibrate(long); + method public void vibrate(long, int); method public void vibrate(long[], int); + method public void vibrate(long[], int, int); } public class WorkSource implements android.os.Parcelable { @@ -23377,6 +23526,13 @@ package android.provider { field public static final java.lang.String URL = "data1"; } + public static final class ContactsContract.ContactCounts { + ctor public ContactsContract.ContactCounts(); + field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras"; + field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts"; + field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles"; + } + protected static abstract interface ContactsContract.ContactNameColumns { field public static final java.lang.String DISPLAY_NAME_ALTERNATIVE = "display_name_alt"; field public static final java.lang.String DISPLAY_NAME_PRIMARY = "display_name"; @@ -26393,6 +26549,28 @@ package android.speech { package android.speech.tts { + public final class Markup implements android.os.Parcelable { + ctor public Markup(); + ctor public Markup(java.lang.String); + ctor public Markup(android.speech.tts.Markup); + method public android.speech.tts.Markup addNestedMarkup(android.speech.tts.Markup); + method public int describeContents(); + method public android.speech.tts.Markup getNestedMarkup(int); + method public java.util.List<android.speech.tts.Markup> getNestedMarkups(); + method public java.lang.String getParameter(java.lang.String); + method public java.lang.String getPlainText(); + method public java.lang.String getType(); + method public static android.speech.tts.Markup markupFromString(java.lang.String) throws java.lang.IllegalArgumentException; + method public int nestedMarkupSize(); + method public int parametersSize(); + method public boolean removeNestedMarkup(android.speech.tts.Markup); + method public void removeParameter(java.lang.String); + method public android.speech.tts.Markup setParameter(java.lang.String, java.lang.String); + method public void setPlainText(java.lang.String); + method public void setType(java.lang.String); + method public void writeToParcel(android.os.Parcel, int); + } + public final class RequestConfig { method public android.os.Bundle getAudioParams(); method public android.speech.tts.VoiceInfo getVoice(); @@ -26455,9 +26633,10 @@ package android.speech.tts { } public final class SynthesisRequestV2 implements android.os.Parcelable { - ctor public SynthesisRequestV2(java.lang.String, java.lang.String, java.lang.String, android.os.Bundle, android.os.Bundle); + ctor public SynthesisRequestV2(android.speech.tts.Markup, java.lang.String, java.lang.String, android.os.Bundle, android.os.Bundle); method public int describeContents(); method public android.os.Bundle getAudioParams(); + method public android.speech.tts.Markup getMarkup(); method public java.lang.String getText(); method public java.lang.String getUtteranceId(); method public java.lang.String getVoiceName(); @@ -26560,7 +26739,9 @@ package android.speech.tts { method public void queueAudio(android.net.Uri, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks); method public void queueSilence(long, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.TextToSpeechClient.RequestCallbacks); method public void queueSpeak(java.lang.String, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks); + method public void queueSpeak(android.speech.tts.Markup, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks); method public void queueSynthesizeToFile(java.lang.String, android.speech.tts.TextToSpeechClient.UtteranceId, java.io.File, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks); + method public void queueSynthesizeToFile(android.speech.tts.Markup, android.speech.tts.TextToSpeechClient.UtteranceId, java.io.File, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks); method public void stop(); } @@ -26634,6 +26815,85 @@ package android.speech.tts { method protected void onVoicesInfoChange(); } + public class Utterance { + ctor public Utterance(); + method public android.speech.tts.Utterance append(android.speech.tts.Utterance.AbstractTts<? extends android.speech.tts.Utterance.AbstractTts<?>>); + method public android.speech.tts.Utterance append(java.lang.String); + method public android.speech.tts.Utterance append(int); + method public android.speech.tts.Markup createMarkup(); + method public android.speech.tts.Utterance.AbstractTts<? extends android.speech.tts.Utterance.AbstractTts<?>> get(int); + method public android.speech.tts.Utterance setNoWarningOnFallback(boolean); + method public int size(); + method public static android.speech.tts.Utterance utteranceFromString(java.lang.String) throws java.lang.IllegalArgumentException; + field public static final int ANIMACY_ANIMATE = 1; // 0x1 + field public static final int ANIMACY_INANIMATE = 2; // 0x2 + field public static final int ANIMACY_UNKNOWN = 0; // 0x0 + field public static final int CASE_ABLATIVE = 4; // 0x4 + field public static final int CASE_ACCUSATIVE = 2; // 0x2 + field public static final int CASE_DATIVE = 3; // 0x3 + field public static final int CASE_GENITIVE = 5; // 0x5 + field public static final int CASE_INSTRUMENTAL = 8; // 0x8 + field public static final int CASE_LOCATIVE = 7; // 0x7 + field public static final int CASE_NOMINATIVE = 1; // 0x1 + field public static final int CASE_UNKNOWN = 0; // 0x0 + field public static final int CASE_VOCATIVE = 6; // 0x6 + field public static final int GENDER_FEMALE = 3; // 0x3 + field public static final int GENDER_MALE = 2; // 0x2 + field public static final int GENDER_NEUTRAL = 1; // 0x1 + field public static final int GENDER_UNKNOWN = 0; // 0x0 + field public static final java.lang.String KEY_NO_WARNING_ON_FALLBACK = "no_warning_on_fallback"; + field public static final int MULTIPLICITY_DUAL = 2; // 0x2 + field public static final int MULTIPLICITY_PLURAL = 3; // 0x3 + field public static final int MULTIPLICITY_SINGLE = 1; // 0x1 + field public static final int MULTIPLICITY_UNKNOWN = 0; // 0x0 + field public static final java.lang.String TYPE_UTTERANCE = "utterance"; + } + + public static abstract class Utterance.AbstractTts { + ctor protected Utterance.AbstractTts(); + ctor protected Utterance.AbstractTts(android.speech.tts.Markup); + method public java.lang.String generatePlainText(); + method public android.speech.tts.Markup getMarkup(); + method protected java.lang.String getParameter(java.lang.String); + method public java.lang.String getPlainText(); + method public java.lang.String getType(); + method protected C removeParameter(java.lang.String); + method protected C setParameter(java.lang.String, java.lang.String); + method public C setPlainText(java.lang.String); + field protected android.speech.tts.Markup mMarkup; + } + + public static abstract class Utterance.AbstractTtsSemioticClass extends android.speech.tts.Utterance.AbstractTts { + ctor protected Utterance.AbstractTtsSemioticClass(); + ctor protected Utterance.AbstractTtsSemioticClass(android.speech.tts.Markup); + method public int getAnimacy(); + method public int getCase(); + method public int getGender(); + method public int getMultiplicity(); + method public C setAnimacy(int); + method public C setCase(int); + method public C setGender(int); + method public C setMultiplicity(int); + } + + public static class Utterance.TtsCardinal extends android.speech.tts.Utterance.AbstractTtsSemioticClass { + ctor public Utterance.TtsCardinal(); + ctor public Utterance.TtsCardinal(int); + ctor public Utterance.TtsCardinal(java.lang.String); + method public java.lang.String getInteger(); + method public android.speech.tts.Utterance.TtsCardinal setInteger(int); + method public android.speech.tts.Utterance.TtsCardinal setInteger(java.lang.String); + field protected static final java.lang.String TYPE_CARDINAL = "cardinal"; + } + + public static class Utterance.TtsText extends android.speech.tts.Utterance.AbstractTtsSemioticClass { + ctor public Utterance.TtsText(); + ctor public Utterance.TtsText(java.lang.String); + method public java.lang.String getText(); + method public android.speech.tts.Utterance.TtsText setText(java.lang.String); + field protected static final java.lang.String TYPE_TEXT = "text"; + } + public abstract class UtteranceProgressListener { ctor public UtteranceProgressListener(); method public abstract void onDone(java.lang.String); @@ -27312,6 +27572,313 @@ package android.system { } +package android.telecomm { + + public final class CallAudioState implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static int ROUTE_ALL; + field public static int ROUTE_BLUETOOTH; + field public static int ROUTE_EARPIECE; + field public static int ROUTE_SPEAKER; + field public static int ROUTE_WIRED_HEADSET; + field public static int ROUTE_WIRED_OR_EARPIECE; + field public final boolean isMuted; + field public final int route; + field public final int supportedRouteMask; + } + + public class CallCapabilities { + ctor public CallCapabilities(); + field public static final int ADD_CALL = 16; // 0x10 + field public static final int ALL = 511; // 0x1ff + field public static final int CONNECTION_HANDOFF = 256; // 0x100 + field public static final int GENERIC_CONFERENCE = 128; // 0x80 + field public static final int HOLD = 1; // 0x1 + field public static final int MERGE_CALLS = 4; // 0x4 + field public static final int MUTE = 64; // 0x40 + field public static final int RESPOND_VIA_TEXT = 32; // 0x20 + field public static final int SUPPORT_HOLD = 2; // 0x2 + field public static final int SWAP_CALLS = 8; // 0x8 + } + + public final class CallInfo implements android.os.Parcelable { + ctor public CallInfo(java.lang.String, android.telecomm.CallState, android.net.Uri); + method public int describeContents(); + method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor(); + method public android.os.Bundle getExtras(); + method public android.telecomm.GatewayInfo getGatewayInfo(); + method public android.net.Uri getHandle(); + method public java.lang.String getId(); + method public android.net.Uri getOriginalHandle(); + method public android.telecomm.CallState getState(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class CallNumberPresentation extends java.lang.Enum { + method public static android.telecomm.CallNumberPresentation valueOf(java.lang.String); + method public static final android.telecomm.CallNumberPresentation[] values(); + enum_constant public static final android.telecomm.CallNumberPresentation ALLOWED; + enum_constant public static final android.telecomm.CallNumberPresentation PAYPHONE; + enum_constant public static final android.telecomm.CallNumberPresentation RESTRICTED; + enum_constant public static final android.telecomm.CallNumberPresentation UNKNOWN; + } + + public abstract class CallService extends android.app.Service { + ctor public CallService(); + method public abstract void abort(java.lang.String); + method public abstract void answer(java.lang.String); + method public abstract void call(android.telecomm.CallInfo); + method public abstract void disconnect(java.lang.String); + method protected final android.telecomm.CallServiceAdapter getAdapter(); + method public final android.os.IBinder getBinder(); + method public abstract void hold(java.lang.String); + method public abstract void isCompatibleWith(android.telecomm.CallInfo); + method protected void onAdapterAttached(android.telecomm.CallServiceAdapter); + method public abstract void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState); + method public final android.os.IBinder onBind(android.content.Intent); + method public abstract void playDtmfTone(java.lang.String, char); + method public abstract void reject(java.lang.String); + method public abstract void setIncomingCallId(java.lang.String, android.os.Bundle); + method public abstract void stopDtmfTone(java.lang.String); + method public abstract void unhold(java.lang.String); + } + + public final class CallServiceAdapter { + method public void handleFailedOutgoingCall(java.lang.String, java.lang.String); + method public void handleSuccessfulOutgoingCall(java.lang.String); + method public void notifyIncomingCall(android.telecomm.CallInfo); + method public void setActive(java.lang.String); + method public void setDialing(java.lang.String); + method public void setDisconnected(java.lang.String, int, java.lang.String); + method public void setIsCompatibleWith(java.lang.String, boolean); + method public void setOnHold(java.lang.String); + method public void setRequestingRingback(java.lang.String, boolean); + method public void setRinging(java.lang.String); + } + + public final class CallServiceDescriptor implements android.os.Parcelable { + method public int describeContents(); + method public java.lang.String getCallServiceId(); + method public int getNetworkType(); + method public android.content.ComponentName getServiceComponent(); + method public static android.telecomm.CallServiceDescriptor.Builder newBuilder(android.content.Context); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_MOBILE = 4; // 0x4 + field public static final int FLAG_PSTN = 2; // 0x2 + field public static final int FLAG_WIFI = 1; // 0x1 + } + + public static class CallServiceDescriptor.Builder { + method public android.telecomm.CallServiceDescriptor build(); + method public android.telecomm.CallServiceDescriptor.Builder setCallService(java.lang.Class<? extends android.telecomm.CallService>); + method public android.telecomm.CallServiceDescriptor.Builder setNetworkType(int); + } + + public final class CallServiceLookupResponse { + method public void setCallServiceDescriptors(java.util.List<android.telecomm.CallServiceDescriptor>); + } + + public abstract class CallServiceProvider extends android.app.Service { + ctor protected CallServiceProvider(); + method public abstract void lookupCallServices(android.telecomm.CallServiceLookupResponse); + method public android.os.IBinder onBind(android.content.Intent); + } + + public abstract class CallServiceSelector extends android.app.Service { + ctor protected CallServiceSelector(); + method protected final void cancelOutgoingCall(android.telecomm.CallInfo); + method protected final android.telecomm.CallServiceSelectorAdapter getAdapter(); + method protected final java.util.Collection<android.telecomm.CallInfo> getCalls(); + method protected void onAdapterAttached(android.telecomm.CallServiceSelectorAdapter); + method public final android.os.IBinder onBind(android.content.Intent); + method protected abstract void select(android.telecomm.CallInfo, java.util.List<android.telecomm.CallServiceDescriptor>); + } + + public final class CallServiceSelectorAdapter { + method public void cancelOutgoingCall(java.lang.String); + method public void setHandoffInfo(java.lang.String, android.net.Uri, android.os.Bundle); + method public void setSelectedCallServices(java.lang.String, java.util.List<android.telecomm.CallServiceDescriptor>); + } + + public final class CallState extends java.lang.Enum { + method public static android.telecomm.CallState valueOf(java.lang.String); + method public static final android.telecomm.CallState[] values(); + enum_constant public static final android.telecomm.CallState ACTIVE; + enum_constant public static final android.telecomm.CallState DIALING; + enum_constant public static final android.telecomm.CallState DISCONNECTED; + enum_constant public static final android.telecomm.CallState NEW; + enum_constant public static final android.telecomm.CallState ON_HOLD; + enum_constant public static final android.telecomm.CallState POST_DIAL; + enum_constant public static final android.telecomm.CallState POST_DIAL_WAIT; + enum_constant public static final android.telecomm.CallState RINGING; + } + + public abstract class Connection { + ctor protected Connection(); + method public final android.telecomm.CallAudioState getCallAudioState(); + method public final android.net.Uri getHandle(); + method public boolean isRequestingRingback(); + method protected void onAbort(); + method protected void onAnswer(); + method protected void onDisconnect(); + method protected void onHold(); + method protected void onPlayDtmfTone(char); + method protected void onReject(); + method protected void onSetAudioState(android.telecomm.CallAudioState); + method protected void onSetSignal(android.os.Bundle); + method protected void onSetState(int); + method protected void onStopDtmfTone(); + method protected void onUnhold(); + method protected void setActive(); + method public void setAudioState(android.telecomm.CallAudioState); + method protected void setDialing(); + method protected void setDisconnected(int, java.lang.String); + method protected void setHandle(android.net.Uri); + method protected void setOnHold(); + method protected void setRequestingRingback(boolean); + method protected void setRinging(); + method public static java.lang.String stateToString(int); + } + + public static abstract interface Connection.Listener { + method public abstract void onAudioStateChanged(android.telecomm.Connection, android.telecomm.CallAudioState); + method public abstract void onDestroyed(android.telecomm.Connection); + method public abstract void onDisconnected(android.telecomm.Connection, int, java.lang.String); + method public abstract void onHandleChanged(android.telecomm.Connection, android.net.Uri); + method public abstract void onRequestingRingback(android.telecomm.Connection, boolean); + method public abstract void onSignalChanged(android.telecomm.Connection, android.os.Bundle); + method public abstract void onStateChanged(android.telecomm.Connection, int); + } + + public static class Connection.ListenerBase implements android.telecomm.Connection.Listener { + ctor public Connection.ListenerBase(); + method public void onAudioStateChanged(android.telecomm.Connection, android.telecomm.CallAudioState); + method public void onDestroyed(android.telecomm.Connection); + method public void onDisconnected(android.telecomm.Connection, int, java.lang.String); + method public void onHandleChanged(android.telecomm.Connection, android.net.Uri); + method public void onRequestingRingback(android.telecomm.Connection, boolean); + method public void onSignalChanged(android.telecomm.Connection, android.os.Bundle); + method public void onStateChanged(android.telecomm.Connection, int); + } + + public final class Connection.State { + field public static final int ACTIVE = 3; // 0x3 + field public static final int DIALING = 2; // 0x2 + field public static final int DISCONNECTED = 5; // 0x5 + field public static final int HOLDING = 4; // 0x4 + field public static final int NEW = 0; // 0x0 + field public static final int RINGING = 1; // 0x1 + } + + public final class ConnectionRequest { + ctor public ConnectionRequest(android.net.Uri, android.os.Bundle); + method public android.os.Bundle getExtras(); + method public android.net.Uri getHandle(); + } + + public abstract class ConnectionService extends android.telecomm.CallService { + ctor public ConnectionService(); + method public final void abort(java.lang.String); + method public final void answer(java.lang.String); + method public final void call(android.telecomm.CallInfo); + method public final void disconnect(java.lang.String); + method public final void hold(java.lang.String); + method public final void isCompatibleWith(android.telecomm.CallInfo); + method public final void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState); + method public void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>); + method public void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>); + method public void onFindSubscriptions(android.net.Uri, android.telecomm.Response<android.net.Uri, android.telecomm.Subscription>); + method public final void playDtmfTone(java.lang.String, char); + method public final void reject(java.lang.String); + method public final void setIncomingCallId(java.lang.String, android.os.Bundle); + method public final void stopDtmfTone(java.lang.String); + method public final void unhold(java.lang.String); + } + + public class GatewayInfo implements android.os.Parcelable { + method public int describeContents(); + method public android.net.Uri getGatewayHandle(); + method public java.lang.String getGatewayProviderPackageName(); + method public android.net.Uri getOriginalHandle(); + method public boolean isEmpty(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class InCallAdapter { + method public void answerCall(java.lang.String); + method public void disconnectCall(java.lang.String); + method public void handoffCall(java.lang.String); + method public void holdCall(java.lang.String); + method public void mute(boolean); + method public void playDtmfTone(java.lang.String, char); + method public void postDialContinue(java.lang.String); + method public void rejectCall(java.lang.String); + method public void setAudioRoute(int); + method public void stopDtmfTone(java.lang.String); + method public void unholdCall(java.lang.String); + } + + public final class InCallCall implements android.os.Parcelable { + method public int describeContents(); + method public int getCapabilities(); + method public long getConnectTimeMillis(); + method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor(); + method public int getDisconnectCause(); + method public android.telecomm.GatewayInfo getGatewayInfo(); + method public android.net.Uri getHandle(); + method public android.telecomm.CallServiceDescriptor getHandoffCallServiceDescriptor(); + method public java.lang.String getId(); + method public android.telecomm.CallState getState(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public abstract class InCallService extends android.app.Service { + ctor protected InCallService(); + method protected abstract void addCall(android.telecomm.InCallCall); + method protected abstract void bringToForeground(boolean); + method protected final android.telecomm.InCallAdapter getAdapter(); + method protected void onAdapterAttached(android.telecomm.InCallAdapter); + method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState); + method public final android.os.IBinder onBind(android.content.Intent); + method protected abstract void setPostDial(java.lang.String, java.lang.String); + method protected abstract void setPostDialWait(java.lang.String, java.lang.String); + method protected abstract void updateCall(android.telecomm.InCallCall); + } + + public abstract interface Response { + method public abstract void onError(IN, java.lang.String); + method public abstract void onResult(IN, OUT...); + } + + public class Subscription implements android.os.Parcelable { + ctor public Subscription(); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public final class TelecommConstants { + ctor public TelecommConstants(); + field public static final java.lang.String ACTION_CALL_SERVICE; + field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER; + field public static final java.lang.String ACTION_CALL_SERVICE_SELECTOR; + field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL"; + field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ',' + field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';' + field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE"; + field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE"; + field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR"; + field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS"; + } + +} + package android.telephony { public final class CellIdentityCdma implements android.os.Parcelable { @@ -27747,12 +28314,8 @@ package android.telephony { method public java.lang.String getVoiceMailAlphaTag(); method public java.lang.String getVoiceMailNumber(); method public boolean hasIccCard(); - method public boolean iccCloseLogicalChannel(int); - method public int iccOpenLogicalChannel(java.lang.String); - method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String); method public boolean isNetworkRoaming(); method public void listen(android.telephony.PhoneStateListener, int); - method public java.lang.String sendEnvelopeWithStatus(java.lang.String); field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE"; field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE"; field public static final int CALL_STATE_IDLE = 0; // 0x0 @@ -32360,6 +32923,7 @@ package android.view { method public void requestLayout(); method public boolean requestRectangleOnScreen(android.graphics.Rect); method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean); + method public final void requestUnbufferedDispatch(android.view.MotionEvent); method public static int resolveSize(int, int); method public static int resolveSizeAndState(int, int, int); method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>); @@ -36057,6 +36621,10 @@ package android.widget { method public void setRowCount(int); method public void setRowOrderPreserved(boolean); method public void setUseDefaultMargins(boolean); + method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment, float); + method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment, float); + method public static android.widget.GridLayout.Spec spec(int, int, float); + method public static android.widget.GridLayout.Spec spec(int, float); method public static android.widget.GridLayout.Spec spec(int, int, android.widget.GridLayout.Alignment); method public static android.widget.GridLayout.Spec spec(int, android.widget.GridLayout.Alignment); method public static android.widget.GridLayout.Spec spec(int, int); diff --git a/api/removed.txt b/api/removed.txt index e69de29..458c422 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -0,0 +1,8 @@ +package android.media { + + public class AudioFormat { + ctor public AudioFormat(); + } + +} + diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 6b55b7b..8945526 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -35,6 +35,7 @@ import android.content.pm.ResolveInfo; import android.graphics.Rect; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -46,6 +47,8 @@ import android.util.AndroidException; import android.view.IWindowManager; import com.android.internal.os.BaseCommand; +import dalvik.system.VMRuntime; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; @@ -94,7 +97,11 @@ public class Am extends BaseCommand { " am broadcast [--user <USER_ID> | all | current] <INTENT>\n" + " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" + " [--user <USER_ID> | current]\n" + - " [--no-window-animation] <COMPONENT>\n" + + " [--no-window-animation]\n" + + " [--abi <ABI>]\n : Launch the instrumented process with the " + + " selected ABI. This assumes that the process supports the" + + " selected ABI." + + " <COMPONENT>\n" + " am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" + " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" + " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" + @@ -835,6 +842,7 @@ public class Am extends BaseCommand { Bundle args = new Bundle(); String argKey = null, argValue = null; IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + String abi = null; String opt; while ((opt=nextOption()) != null) { @@ -853,6 +861,8 @@ public class Am extends BaseCommand { no_window_animation = true; } else if (opt.equals("--user")) { userId = parseUserArg(nextArgRequired()); + } else if (opt.equals("--abi")) { + abi = nextArgRequired(); } else { System.err.println("Error: Unknown option: " + opt); return; @@ -883,7 +893,24 @@ public class Am extends BaseCommand { wm.setAnimationScale(1, 0.0f); } - if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId)) { + if (abi != null) { + final String[] supportedAbis = Build.SUPPORTED_ABIS; + boolean matched = false; + for (String supportedAbi : supportedAbis) { + if (supportedAbi.equals(abi)) { + matched = true; + break; + } + } + + if (!matched) { + throw new AndroidException( + "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi); + } + } + + if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, + abi)) { throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString()); } diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java index 2673031..4503726 100644 --- a/cmds/bu/src/com/android/commands/bu/Backup.java +++ b/cmds/bu/src/com/android/commands/bu/Backup.java @@ -20,6 +20,7 @@ import android.app.backup.IBackupManager; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.system.OsConstants; import android.util.Log; import java.io.IOException; @@ -50,13 +51,11 @@ public final class Backup { return; } - int socketFd = Integer.parseInt(nextArg()); - String arg = nextArg(); if (arg.equals("backup")) { - doFullBackup(socketFd); + doFullBackup(OsConstants.STDOUT_FILENO); } else if (arg.equals("restore")) { - doFullRestore(socketFd); + doFullRestore(OsConstants.STDIN_FILENO); } else { Log.e(TAG, "Invalid operation '" + arg + "'"); } diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 5454b46..47047b8 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -39,6 +39,7 @@ import android.content.pm.VerificationParams; import android.content.res.AssetManager; import android.content.res.Resources; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.IUserManager; import android.os.Process; @@ -57,7 +58,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.WeakHashMap; - import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; @@ -823,6 +823,7 @@ public final class Pm { byte[] tag = null; String originatingUriString = null; String referrer = null; + String abi = null; while ((opt=nextOption()) != null) { if (opt.equals("-l")) { @@ -893,12 +894,34 @@ public final class Pm { System.err.println("Error: must supply argument for --referrer"); return; } + } else if (opt.equals("--abi")) { + abi = nextOptionData(); + if (abi == null) { + System.err.println("Error: must supply argument for --abi"); + return; + } } else { System.err.println("Error: Unknown option: " + opt); return; } } + if (abi != null) { + final String[] supportedAbis = Build.SUPPORTED_ABIS; + boolean matched = false; + for (String supportedAbi : supportedAbis) { + if (supportedAbi.equals(abi)) { + matched = true; + break; + } + } + + if (!matched) { + System.err.println("Error: abi " + abi + " not supported on this device."); + return; + } + } + final ContainerEncryptionParams encryptionParams; if (algo != null || iv != null || key != null || macAlgo != null || macKey != null || tag != null) { @@ -976,8 +999,9 @@ public final class Pm { VerificationParams verificationParams = new VerificationParams(verificationURI, originatingURI, referrerURI, VerificationParams.NO_UID, null); - mPm.installPackageWithVerificationAndEncryptionEtc(apkURI, null, obs, installFlags, - installerPackageName, verificationParams, encryptionParams); + mPm.installPackageWithVerificationEncryptionAndAbiOverrideEtc(apkURI, null, + obs, installFlags, installerPackageName, verificationParams, + encryptionParams, abi); synchronized (obs) { while (!obs.finished) { @@ -1146,12 +1170,22 @@ public final class Pm { } private void runUninstall() { - int unInstallFlags = PackageManager.DELETE_ALL_USERS; + int unInstallFlags = 0; + int userId = UserHandle.USER_ALL; String opt; while ((opt=nextOption()) != null) { if (opt.equals("-k")) { unInstallFlags |= PackageManager.DELETE_KEEP_DATA; + } else if (opt.equals("--user")) { + String param = nextArg(); + if (isNumber(param)) { + userId = Integer.parseInt(param); + } else { + showUsage(); + System.err.println("Error: Invalid user: " + param); + return; + } } else { System.err.println("Error: Unknown option: " + opt); return; @@ -1164,7 +1198,34 @@ public final class Pm { showUsage(); return; } - boolean result = deletePackage(pkg, unInstallFlags); + + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_OWNER; + unInstallFlags |= PackageManager.DELETE_ALL_USERS; + } else { + PackageInfo info; + try { + info = mPm.getPackageInfo(pkg, 0, userId); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(PM_NOT_RUNNING_ERR); + return; + } + if (info == null) { + System.err.println("Failure - not installed for " + userId); + return; + } + final boolean isSystem = + (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + // If we are being asked to delete a system app for just one + // user set flag so it disables rather than reverting to system + // version of the app. + if (isSystem) { + unInstallFlags |= PackageManager.DELETE_SYSTEM_APP; + } + } + + boolean result = deletePackage(pkg, unInstallFlags, userId); if (result) { System.out.println("Success"); } else { @@ -1172,10 +1233,10 @@ public final class Pm { } } - private boolean deletePackage(String pkg, int unInstallFlags) { + private boolean deletePackage(String pkg, int unInstallFlags, int userId) { PackageDeleteObserver obs = new PackageDeleteObserver(); try { - mPm.deletePackageAsUser(pkg, obs, UserHandle.USER_OWNER, unInstallFlags); + mPm.deletePackageAsUser(pkg, obs, userId, unInstallFlags); synchronized (obs) { while (!obs.finished) { @@ -1571,7 +1632,7 @@ public final class Pm { System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]"); System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]"); System.err.println(" [--originating-uri <URI>] [--referrer <URI>] PATH"); - System.err.println(" pm uninstall [-k] PACKAGE"); + System.err.println(" pm uninstall [-k] [--user USER_ID] PACKAGE"); System.err.println(" pm clear [--user USER_ID] PACKAGE"); System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT"); @@ -1585,7 +1646,7 @@ public final class Pm { System.err.println(" pm get-install-location"); System.err.println(" pm set-permission-enforced PERMISSION [true|false]"); System.err.println(" pm trim-caches DESIRED_FREE_SPACE"); - System.err.println(" pm create-user [--relatedTo USER_ID] [--managed] USER_NAME"); + System.err.println(" pm create-user [--profileOf USER_ID] [--managed] USER_NAME"); System.err.println(" pm remove-user USER_ID"); System.err.println(" pm get-max-users"); System.err.println(""); diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 2efe4d3..6296dd1 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -141,7 +141,7 @@ int main(int argc, char** argv) ScreenshotClient screenshot; sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId); - if (display != NULL && screenshot.update(display, false) == NO_ERROR) { + if (display != NULL && screenshot.update(display, Rect(), false) == NO_ERROR) { base = screenshot.getPixels(); w = screenshot.getWidth(); h = screenshot.getHeight(); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 0f65454..56462ae 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -943,7 +943,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM b = data.readStrongBinder(); IUiAutomationConnection c = IUiAutomationConnection.Stub.asInterface(b); int userId = data.readInt(); - boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId); + String abiOverride = data.readString(); + boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId, + abiOverride); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -3339,7 +3341,8 @@ class ActivityManagerProxy implements IActivityManager public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher, - IUiAutomationConnection connection, int userId) throws RemoteException { + IUiAutomationConnection connection, int userId, String instructionSet) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -3350,6 +3353,7 @@ class ActivityManagerProxy implements IActivityManager data.writeStrongBinder(watcher != null ? watcher.asBinder() : null); data.writeStrongBinder(connection != null ? connection.asBinder() : null); data.writeInt(userId); + data.writeString(instructionSet); mRemote.transact(START_INSTRUMENTATION_TRANSACTION, data, reply, 0); reply.readException(); boolean res = reply.readInt() != 0; diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 2f35160..84673d9 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1455,10 +1455,10 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig, - int userIdDest) { + public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, + int sourceUserId, int targetUserId) { try { - mPM.addForwardingIntentFilter(filter, removable, userIdOrig, userIdDest); + mPM.addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); } catch (RemoteException e) { // Should never happen! } @@ -1468,14 +1468,31 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void clearForwardingIntentFilters(int userIdOrig) { + public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId, + int targetUserId) { + addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); + } + + /** + * @hide + */ + @Override + public void clearCrossProfileIntentFilters(int sourceUserId) { try { - mPM.clearForwardingIntentFilters(userIdOrig); + mPM.clearCrossProfileIntentFilters(sourceUserId); } catch (RemoteException e) { // Should never happen! } } + /** + * @hide + */ + @Override + public void clearForwardingIntentFilters(int sourceUserId) { + clearCrossProfileIntentFilters(sourceUserId); + } + private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ac25a53..e03224c 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -35,7 +35,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IIntentReceiver; import android.content.IntentSender; +import android.content.IRestrictionsManager; import android.content.ReceiverCallNotAllowedException; +import android.content.RestrictionsManager; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; @@ -662,6 +664,13 @@ class ContextImpl extends Context { } }); + registerService(RESTRICTIONS_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(RESTRICTIONS_SERVICE); + IRestrictionsManager service = IRestrictionsManager.Stub.asInterface(b); + return new RestrictionsManager(ctx, service); + } + }); registerService(PRINT_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder iBinder = ServiceManager.getService(Context.PRINT_SERVICE); @@ -1755,7 +1764,8 @@ class ContextImpl extends Context { arguments.setAllowFds(false); } return ActivityManagerNative.getDefault().startInstrumentation( - className, profileFile, 0, arguments, null, null, getUserId()); + className, profileFile, 0, arguments, null, null, getUserId(), + null /* ABI override */); } catch (RemoteException e) { // System has crashed, nothing we can do. } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 8434c2a..bf2d7e5 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -176,7 +176,8 @@ public interface IActivityManager extends IInterface { public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher, - IUiAutomationConnection connection, int userId) throws RemoteException; + IUiAutomationConnection connection, int userId, + String abiOverride) throws RemoteException; public void finishInstrumentation(IApplicationThread target, int resultCode, Bundle results) throws RemoteException; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 77b1acf..afe2981 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -25,6 +25,7 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.RestrictionsManager; import android.os.Bundle; import android.os.Handler; import android.os.Process; @@ -103,6 +104,17 @@ public class DevicePolicyManager { = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE"; /** + * A broadcast intent with this action can be sent to ManagedProvisionning to specify that the + * user has already consented to the creation of the managed profile. + * The intent must contain the extras + * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} and + * {@link #EXTRA_PROVISIONING_TOKEN} + * @hide + */ + public static final String ACTION_PROVISIONING_USER_HAS_CONSENTED + = "android.app.action.USER_HAS_CONSENTED"; + + /** * A String extra holding the name of the package of the mobile device management application * that starts the managed provisioning flow. This package will be set as the profile owner. * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}. @@ -111,6 +123,13 @@ public class DevicePolicyManager { = "deviceAdminPackageName"; /** + * An int extra used to identify the consent of the user to create the managed profile. + * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} + */ + public static final String EXTRA_PROVISIONING_TOKEN + = "android.app.extra.token"; + + /** * A String extra holding the default name of the profile that is created during managed profile * provisioning. * <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} @@ -176,15 +195,16 @@ public class DevicePolicyManager { public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; /** - * Flag for {@link #addForwardingIntentFilter}: the intents will forwarded to the primary user. + * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from a + * managed profile to its parent. */ - public static int FLAG_TO_PRIMARY_USER = 0x0001; + public static int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001; /** - * Flag for {@link #addForwardingIntentFilter}: the intents will be forwarded to the managed - * profile. + * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from the + * parent to its managed profile. */ - public static int FLAG_TO_MANAGED_PROFILE = 0x0002; + public static int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002; /** * Return true if the given administrator component is currently @@ -1951,17 +1971,16 @@ public class DevicePolicyManager { } /** - * Called by a profile owner to forward intents sent from the managed profile to the owner, or - * from the owner to the managed profile. - * If an intent matches this intent filter, then activities belonging to the other user can - * respond to this intent. + * Called by the profile owner so that some intents sent in the managed profile can also be + * resolved in the parent, or vice versa. * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param filter if an intent matches this IntentFilter, then it can be forwarded. + * @param filter The {@link IntentFilter} the intent has to match to be also resolved in the + * other profile */ - public void addForwardingIntentFilter(ComponentName admin, IntentFilter filter, int flags) { + public void addCrossProfileIntentFilter(ComponentName admin, IntentFilter filter, int flags) { if (mService != null) { try { - mService.addForwardingIntentFilter(admin, filter, flags); + mService.addCrossProfileIntentFilter(admin, filter, flags); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1969,14 +1988,14 @@ public class DevicePolicyManager { } /** - * Called by a profile owner to remove the forwarding intent filters from the current user - * and from the owner. + * Called by a profile owner to remove the cross-profile intent filters from the managed profile + * and from the parent. * @param admin Which {@link DeviceAdminReceiver} this request is associated with. */ - public void clearForwardingIntentFilters(ComponentName admin) { + public void clearCrossProfileIntentFilters(ComponentName admin) { if (mService != null) { try { - mService.clearForwardingIntentFilters(admin); + mService.clearCrossProfileIntentFilters(admin); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -2320,4 +2339,23 @@ public class DevicePolicyManager { } } + /** + * Designates a specific broadcast receiver component as the provider for + * making permission requests of a local or remote administrator of the user. + * <p/> + * Only a profile owner can designate the restrictions provider. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param receiver The component name of a BroadcastReceiver that handles the + * {@link RestrictionsManager#ACTION_REQUEST_PERMISSION} intent. If this param is null, + * it removes the restrictions provider previously assigned. + */ + public void setRestrictionsProvider(ComponentName admin, ComponentName receiver) { + if (mService != null) { + try { + mService.setRestrictionsProvider(admin, receiver); + } catch (RemoteException re) { + Log.w(TAG, "Failed to set permission provider on device policy service"); + } + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 3c08c14..7f754dc 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -121,9 +121,12 @@ interface IDevicePolicyManager { void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings); Bundle getApplicationRestrictions(in ComponentName who, in String packageName); + void setRestrictionsProvider(in ComponentName who, in ComponentName provider); + ComponentName getRestrictionsProvider(int userHandle); + void setUserRestriction(in ComponentName who, in String key, boolean enable); - void addForwardingIntentFilter(in ComponentName admin, in IntentFilter filter, int flags); - void clearForwardingIntentFilters(in ComponentName admin); + void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags); + void clearCrossProfileIntentFilters(in ComponentName admin); boolean setApplicationBlocked(in ComponentName admin, in String packageName, boolean blocked); int setApplicationsBlocked(in ComponentName admin, in Intent intent, boolean blocked); diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java new file mode 100644 index 0000000..46f082e --- /dev/null +++ b/core/java/android/app/backup/BackupTransport.java @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.backup; + +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; + +import com.android.internal.backup.IBackupTransport; + +/** + * Concrete class that provides a stable-API bridge between IBackupTransport + * and its implementations. + * + * @hide + */ +public class BackupTransport { + public static final int TRANSPORT_OK = 0; + public static final int TRANSPORT_ERROR = 1; + public static final int TRANSPORT_NOT_INITIALIZED = 2; + public static final int TRANSPORT_PACKAGE_REJECTED = 3; + public static final int AGENT_ERROR = 4; + public static final int AGENT_UNKNOWN = 5; + + IBackupTransport mBinderImpl = new TransportImpl(); + /** @hide */ + public IBinder getBinder() { + return mBinderImpl.asBinder(); + } + + // ------------------------------------------------------------------------------------ + // Transport self-description and general configuration interfaces + // + + /** + * Ask the transport for the name under which it should be registered. This will + * typically be its host service's component name, but need not be. + */ + public String name() { + throw new UnsupportedOperationException("Transport name() not implemented"); + } + + /** + * Ask the transport for an Intent that can be used to launch any internal + * configuration Activity that it wishes to present. For example, the transport + * may offer a UI for allowing the user to supply login credentials for the + * transport's off-device backend. + * + * If the transport does not supply any user-facing configuration UI, it should + * return null from this method. + * + * @return An Intent that can be passed to Context.startActivity() in order to + * launch the transport's configuration UI. This method will return null + * if the transport does not offer any user-facing configuration UI. + */ + public Intent configurationIntent() { + return null; + } + + /** + * On demand, supply a one-line string that can be shown to the user that + * describes the current backend destination. For example, a transport that + * can potentially associate backup data with arbitrary user accounts should + * include the name of the currently-active account here. + * + * @return A string describing the destination to which the transport is currently + * sending data. This method should not return null. + */ + public String currentDestinationString() { + throw new UnsupportedOperationException( + "Transport currentDestinationString() not implemented"); + } + + /** + * Ask the transport where, on local device storage, to keep backup state blobs. + * This is per-transport so that mock transports used for testing can coexist with + * "live" backup services without interfering with the live bookkeeping. The + * returned string should be a name that is expected to be unambiguous among all + * available backup transports; the name of the class implementing the transport + * is a good choice. + * + * @return A unique name, suitable for use as a file or directory name, that the + * Backup Manager could use to disambiguate state files associated with + * different backup transports. + */ + public String transportDirName() { + throw new UnsupportedOperationException( + "Transport transportDirName() not implemented"); + } + + // ------------------------------------------------------------------------------------ + // Device-level operations common to both key/value and full-data storage + + /** + * Initialize the server side storage for this device, erasing all stored data. + * The transport may send the request immediately, or may buffer it. After + * this is called, {@link #finishBackup} will be called to ensure the request + * is sent and received successfully. + * + * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far) or + * {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure). + */ + public int initializeDevice() { + return BackupTransport.TRANSPORT_ERROR; + } + + /** + * Erase the given application's data from the backup destination. This clears + * out the given package's data from the current backup set, making it as though + * the app had never yet been backed up. After this is called, {@link finishBackup} + * must be called to ensure that the operation is recorded successfully. + * + * @return the same error codes as {@link #performBackup}. + */ + public int clearBackupData(PackageInfo packageInfo) { + return BackupTransport.TRANSPORT_ERROR; + } + + /** + * Finish sending application data to the backup destination. This must be + * called after {@link #performBackup}, {@link #performFullBackup}, or {@link clearBackupData} + * to ensure that all data is sent and the operation properly finalized. Only when this + * method returns true can a backup be assumed to have succeeded. + * + * @return the same error codes as {@link #performBackup} or {@link #performFullBackup}. + */ + public int finishBackup() { + return BackupTransport.TRANSPORT_ERROR; + } + + // ------------------------------------------------------------------------------------ + // Key/value incremental backup support interfaces + + /** + * Verify that this is a suitable time for a key/value backup pass. This should return zero + * if a backup is reasonable right now, some positive value otherwise. This method + * will be called outside of the {@link #performBackup}/{@link #finishBackup} pair. + * + * <p>If this is not a suitable time for a backup, the transport should return a + * backoff delay, in milliseconds, after which the Backup Manager should try again. + * + * @return Zero if this is a suitable time for a backup pass, or a positive time delay + * in milliseconds to suggest deferring the backup pass for a while. + */ + public long requestBackupTime() { + return 0; + } + + /** + * Send one application's key/value data update to the backup destination. The + * transport may send the data immediately, or may buffer it. After this is called, + * {@link #finishBackup} will be called to ensure the data is sent and recorded successfully. + * + * @param packageInfo The identity of the application whose data is being backed up. + * This specifically includes the signature list for the package. + * @param data The data stream that resulted from invoking the application's + * BackupService.doBackup() method. This may be a pipe rather than a file on + * persistent media, so it may not be seekable. + * @param wipeAllFirst When true, <i>all</i> backed-up data for the current device/account + * must be erased prior to the storage of the data provided here. The purpose of this + * is to provide a guarantee that no stale data exists in the restore set when the + * device begins providing incremental backups. + * @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far), + * {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure), or + * {@link BackupTransport#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has + * become lost due to inactivity purge or some other reason and needs re-initializing) + */ + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) { + return BackupTransport.TRANSPORT_ERROR; + } + + // ------------------------------------------------------------------------------------ + // Key/value dataset restore interfaces + + /** + * Get the set of all backups currently available over this transport. + * + * @return Descriptions of the set of restore images available for this device, + * or null if an error occurred (the attempt should be rescheduled). + **/ + public RestoreSet[] getAvailableRestoreSets() { + return null; + } + + /** + * Get the identifying token of the backup set currently being stored from + * this device. This is used in the case of applications wishing to restore + * their last-known-good data. + * + * @return A token that can be passed to {@link #startRestore}, or 0 if there + * is no backup set available corresponding to the current device state. + */ + public long getCurrentRestoreSet() { + return 0; + } + + /** + * Start restoring application data from backup. After calling this function, + * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData} + * to walk through the actual application data. + * + * @param token A backup token as returned by {@link #getAvailableRestoreSets} + * or {@link #getCurrentRestoreSet}. + * @param packages List of applications to restore (if data is available). + * Application data will be restored in the order given. + * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far, call + * {@link #nextRestorePackage}) or {@link BackupTransport#TRANSPORT_ERROR} + * (an error occurred, the restore should be aborted and rescheduled). + */ + public int startRestore(long token, PackageInfo[] packages) { + return BackupTransport.TRANSPORT_ERROR; + } + + /** + * Get the package name of the next application with data in the backup store. + * + * @return The name of one of the packages supplied to {@link #startRestore}, + * or "" (the empty string) if no more backup data is available, + * or null if an error occurred (the restore should be aborted and rescheduled). + */ + public String nextRestorePackage() { + return null; + } + + /** + * Get the data for the application returned by {@link #nextRestorePackage}. + * @param data An open, writable file into which the backup data should be stored. + * @return the same error codes as {@link #startRestore}. + */ + public int getRestoreData(ParcelFileDescriptor outFd) { + return BackupTransport.TRANSPORT_ERROR; + } + + /** + * End a restore session (aborting any in-process data transfer as necessary), + * freeing any resources and connections used during the restore process. + */ + public void finishRestore() { + throw new UnsupportedOperationException( + "Transport finishRestore() not implemented"); + } + + // ------------------------------------------------------------------------------------ + // Full backup interfaces + + /** + * Verify that this is a suitable time for a full-data backup pass. This should return zero + * if a backup is reasonable right now, some positive value otherwise. This method + * will be called outside of the {@link #performFullBackup}/{@link #finishBackup} pair. + * + * <p>If this is not a suitable time for a backup, the transport should return a + * backoff delay, in milliseconds, after which the Backup Manager should try again. + * + * @return Zero if this is a suitable time for a backup pass, or a positive time delay + * in milliseconds to suggest deferring the backup pass for a while. + * + * @see #requestBackupTime() + */ + public long requestFullBackupTime() { + return 0; + } + + /** + * Begin the process of sending an application's full-data archive to the backend. + * The description of the package whose data will be delivered is provided, as well as + * the socket file descriptor on which the transport will receive the data itself. + * + * <p>If the package is not eligible for backup, the transport should return + * {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED}. In this case the system will + * simply proceed with the next candidate if any, or finish the full backup operation + * if all apps have been processed. + * + * <p>After the transport returns {@link BackupTransport#TRANSPORT_OK} from this + * method, the OS will proceed to call {@link #sendBackupData()} one or more times + * to deliver the application's data as a streamed tarball. The transport should not + * read() from the socket except as instructed to via the {@link #sendBackupData(int)} + * method. + * + * <p>After all data has been delivered to the transport, the system will call + * {@link #finishBackup()}. At this point the transport should commit the data to + * its datastore, if appropriate, and close the socket that had been provided in + * {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}. + * + * @param targetPackage The package whose data is to follow. + * @param socket The socket file descriptor through which the data will be provided. + * If the transport returns {@link #TRANSPORT_PACKAGE_REJECTED} here, it must still + * close this file descriptor now; otherwise it should be cached for use during + * succeeding calls to {@link #sendBackupData(int)}, and closed in response to + * {@link #finishBackup()}. + * @return TRANSPORT_PACKAGE_REJECTED to indicate that the stated application is not + * to be backed up; TRANSPORT_OK to indicate that the OS may proceed with delivering + * backup data; TRANSPORT_ERROR to indicate a fatal error condition that precludes + * performing a backup at this time. + */ + public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) { + return BackupTransport.TRANSPORT_PACKAGE_REJECTED; + } + + /** + * Tells the transport to read {@code numBytes} bytes of data from the socket file + * descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)} + * call, and deliver those bytes to the datastore. + * + * @param numBytes The number of bytes of tarball data available to be read from the + * socket. + * @return TRANSPORT_OK on successful processing of the data; TRANSPORT_ERROR to + * indicate a fatal error situation. If an error is returned, the system will + * call finishBackup() and stop attempting backups until after a backoff and retry + * interval. + */ + public int sendBackupData(int numBytes) { + return BackupTransport.TRANSPORT_ERROR; + } + + /** + * Bridge between the actual IBackupTransport implementation and the stable API. If the + * binder interface needs to change, we use this layer to translate so that we can + * (if appropriate) decouple those framework-side changes from the BackupTransport + * implementations. + */ + class TransportImpl extends IBackupTransport.Stub { + + @Override + public String name() throws RemoteException { + return BackupTransport.this.name(); + } + + @Override + public Intent configurationIntent() throws RemoteException { + return BackupTransport.this.configurationIntent(); + } + + @Override + public String currentDestinationString() throws RemoteException { + return BackupTransport.this.currentDestinationString(); + } + + @Override + public String transportDirName() throws RemoteException { + return BackupTransport.this.transportDirName(); + } + + @Override + public long requestBackupTime() throws RemoteException { + return BackupTransport.this.requestBackupTime(); + } + + @Override + public int initializeDevice() throws RemoteException { + return BackupTransport.this.initializeDevice(); + } + + @Override + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) + throws RemoteException { + return BackupTransport.this.performBackup(packageInfo, inFd); + } + + @Override + public int clearBackupData(PackageInfo packageInfo) throws RemoteException { + return BackupTransport.this.clearBackupData(packageInfo); + } + + @Override + public int finishBackup() throws RemoteException { + return BackupTransport.this.finishBackup(); + } + + @Override + public RestoreSet[] getAvailableRestoreSets() throws RemoteException { + return BackupTransport.this.getAvailableRestoreSets(); + } + + @Override + public long getCurrentRestoreSet() throws RemoteException { + return BackupTransport.this.getCurrentRestoreSet(); + } + + @Override + public int startRestore(long token, PackageInfo[] packages) throws RemoteException { + return BackupTransport.this.startRestore(token, packages); + } + + @Override + public String nextRestorePackage() throws RemoteException { + return BackupTransport.this.nextRestorePackage(); + } + + @Override + public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException { + return BackupTransport.this.getRestoreData(outFd); + } + + @Override + public void finishRestore() throws RemoteException { + BackupTransport.this.finishRestore(); + } + } +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 1574090..d898060 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -104,6 +104,12 @@ public interface BluetoothProfile { public static final int MAP = 9; /** + * A2DP Sink Profile + * @hide + */ + public static final int A2DP_SINK = 10; + + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile * @hide diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index e8885bf..a040efb 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2695,6 +2695,15 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a + * {@link android.content.RestrictionsManager} for retrieving application restrictions + * and requesting permissions for restricted operations. + * @see #getSystemService + * @see android.content.RestrictionsManager + */ + public static final String RESTRICTIONS_SERVICE = "restrictions"; + + /** + * Use with {@link #getSystemService} to retrieve a * {@link android.app.AppOpsManager} for tracking application operations * on the device. * diff --git a/core/java/android/content/IRestrictionsManager.aidl b/core/java/android/content/IRestrictionsManager.aidl new file mode 100644 index 0000000..b1c0a3a --- /dev/null +++ b/core/java/android/content/IRestrictionsManager.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.os.Bundle; + +/** + * Interface used by the RestrictionsManager + * @hide + */ +interface IRestrictionsManager { + Bundle getApplicationRestrictions(in String packageName); + boolean hasRestrictionsProvider(); + void requestPermission(in String packageName, in String requestTemplate, in Bundle requestData); + void notifyPermissionResponse(in String packageName, in Bundle response); +} diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java index 3ff53bf..62f88a9 100644 --- a/core/java/android/content/RestrictionEntry.java +++ b/core/java/android/content/RestrictionEntry.java @@ -73,32 +73,38 @@ public class RestrictionEntry implements Parcelable { */ public static final int TYPE_MULTI_SELECT = 4; + /** + * A type of restriction. Use this for storing an integer value. The range of values + * is from {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}. + */ + public static final int TYPE_INTEGER = 5; + /** The type of restriction. */ - private int type; + private int mType; /** The unique key that identifies the restriction. */ - private String key; + private String mKey; /** The user-visible title of the restriction. */ - private String title; + private String mTitle; /** The user-visible secondary description of the restriction. */ - private String description; + private String mDescription; /** The user-visible set of choices used for single-select and multi-select lists. */ - private String [] choices; + private String [] mChoiceEntries; /** The values corresponding to the user-visible choices. The value(s) of this entry will * one or more of these, returned by {@link #getAllSelectedStrings()} and * {@link #getSelectedString()}. */ - private String [] values; + private String [] mChoiceValues; /* The chosen value, whose content depends on the type of the restriction. */ - private String currentValue; + private String mCurrentValue; /* List of selected choices in the multi-select case. */ - private String[] currentValues; + private String[] mCurrentValues; /** * Constructor for {@link #TYPE_CHOICE} type. @@ -106,9 +112,9 @@ public class RestrictionEntry implements Parcelable { * @param selectedString the current value */ public RestrictionEntry(String key, String selectedString) { - this.key = key; - this.type = TYPE_CHOICE; - this.currentValue = selectedString; + this.mKey = key; + this.mType = TYPE_CHOICE; + this.mCurrentValue = selectedString; } /** @@ -117,8 +123,8 @@ public class RestrictionEntry implements Parcelable { * @param selectedState whether this restriction is selected or not */ public RestrictionEntry(String key, boolean selectedState) { - this.key = key; - this.type = TYPE_BOOLEAN; + this.mKey = key; + this.mType = TYPE_BOOLEAN; setSelectedState(selectedState); } @@ -128,9 +134,20 @@ public class RestrictionEntry implements Parcelable { * @param selectedStrings the list of values that are currently selected */ public RestrictionEntry(String key, String[] selectedStrings) { - this.key = key; - this.type = TYPE_MULTI_SELECT; - this.currentValues = selectedStrings; + this.mKey = key; + this.mType = TYPE_MULTI_SELECT; + this.mCurrentValues = selectedStrings; + } + + /** + * Constructor for {@link #TYPE_INTEGER} type. + * @param key the unique key for this restriction + * @param selectedInt the integer value of the restriction + */ + public RestrictionEntry(String key, int selectedInt) { + mKey = key; + mType = TYPE_INTEGER; + setIntValue(selectedInt); } /** @@ -138,7 +155,7 @@ public class RestrictionEntry implements Parcelable { * @param type the type for this restriction. */ public void setType(int type) { - this.type = type; + this.mType = type; } /** @@ -146,7 +163,7 @@ public class RestrictionEntry implements Parcelable { * @return the type for this restriction */ public int getType() { - return type; + return mType; } /** @@ -155,7 +172,7 @@ public class RestrictionEntry implements Parcelable { * single string values. */ public String getSelectedString() { - return currentValue; + return mCurrentValue; } /** @@ -164,7 +181,7 @@ public class RestrictionEntry implements Parcelable { * null otherwise. */ public String[] getAllSelectedStrings() { - return currentValues; + return mCurrentValues; } /** @@ -172,7 +189,23 @@ public class RestrictionEntry implements Parcelable { * @return the current selected state of the entry. */ public boolean getSelectedState() { - return Boolean.parseBoolean(currentValue); + return Boolean.parseBoolean(mCurrentValue); + } + + /** + * Returns the value of the entry as an integer when the type is {@link #TYPE_INTEGER}. + * @return the integer value of the entry. + */ + public int getIntValue() { + return Integer.parseInt(mCurrentValue); + } + + /** + * Sets the integer value of the entry when the type is {@link #TYPE_INTEGER}. + * @param value the integer value to set. + */ + public void setIntValue(int value) { + mCurrentValue = Integer.toString(value); } /** @@ -181,7 +214,7 @@ public class RestrictionEntry implements Parcelable { * @param selectedString the string value to select. */ public void setSelectedString(String selectedString) { - currentValue = selectedString; + mCurrentValue = selectedString; } /** @@ -190,7 +223,7 @@ public class RestrictionEntry implements Parcelable { * @param state the current selected state */ public void setSelectedState(boolean state) { - currentValue = Boolean.toString(state); + mCurrentValue = Boolean.toString(state); } /** @@ -199,7 +232,7 @@ public class RestrictionEntry implements Parcelable { * @param allSelectedStrings the current list of selected values. */ public void setAllSelectedStrings(String[] allSelectedStrings) { - currentValues = allSelectedStrings; + mCurrentValues = allSelectedStrings; } /** @@ -216,7 +249,7 @@ public class RestrictionEntry implements Parcelable { * @see #getAllSelectedStrings() */ public void setChoiceValues(String[] choiceValues) { - values = choiceValues; + mChoiceValues = choiceValues; } /** @@ -227,7 +260,7 @@ public class RestrictionEntry implements Parcelable { * @see #setChoiceValues(String[]) */ public void setChoiceValues(Context context, int stringArrayResId) { - values = context.getResources().getStringArray(stringArrayResId); + mChoiceValues = context.getResources().getStringArray(stringArrayResId); } /** @@ -235,7 +268,7 @@ public class RestrictionEntry implements Parcelable { * @return the list of possible values. */ public String[] getChoiceValues() { - return values; + return mChoiceValues; } /** @@ -248,7 +281,7 @@ public class RestrictionEntry implements Parcelable { * @see #setChoiceValues(String[]) */ public void setChoiceEntries(String[] choiceEntries) { - choices = choiceEntries; + mChoiceEntries = choiceEntries; } /** Sets a list of strings that will be presented as choices to the user. This is similar to @@ -257,7 +290,7 @@ public class RestrictionEntry implements Parcelable { * @param stringArrayResId the resource id of a string array containing the possible entries. */ public void setChoiceEntries(Context context, int stringArrayResId) { - choices = context.getResources().getStringArray(stringArrayResId); + mChoiceEntries = context.getResources().getStringArray(stringArrayResId); } /** @@ -265,7 +298,7 @@ public class RestrictionEntry implements Parcelable { * @return the list of choices presented to the user. */ public String[] getChoiceEntries() { - return choices; + return mChoiceEntries; } /** @@ -273,7 +306,7 @@ public class RestrictionEntry implements Parcelable { * @return the user-visible description, null if none was set earlier. */ public String getDescription() { - return description; + return mDescription; } /** @@ -283,7 +316,7 @@ public class RestrictionEntry implements Parcelable { * @param description the user-visible description string. */ public void setDescription(String description) { - this.description = description; + this.mDescription = description; } /** @@ -291,7 +324,7 @@ public class RestrictionEntry implements Parcelable { * @return the key for the restriction. */ public String getKey() { - return key; + return mKey; } /** @@ -299,7 +332,7 @@ public class RestrictionEntry implements Parcelable { * @return the user-visible title for the entry, null if none was set earlier. */ public String getTitle() { - return title; + return mTitle; } /** @@ -307,7 +340,7 @@ public class RestrictionEntry implements Parcelable { * @param title the user-visible title for the entry. */ public void setTitle(String title) { - this.title = title; + this.mTitle = title; } private boolean equalArrays(String[] one, String[] other) { @@ -324,23 +357,23 @@ public class RestrictionEntry implements Parcelable { if (!(o instanceof RestrictionEntry)) return false; final RestrictionEntry other = (RestrictionEntry) o; // Make sure that either currentValue matches or currentValues matches. - return type == other.type && key.equals(other.key) + return mType == other.mType && mKey.equals(other.mKey) && - ((currentValues == null && other.currentValues == null - && currentValue != null && currentValue.equals(other.currentValue)) + ((mCurrentValues == null && other.mCurrentValues == null + && mCurrentValue != null && mCurrentValue.equals(other.mCurrentValue)) || - (currentValue == null && other.currentValue == null - && currentValues != null && equalArrays(currentValues, other.currentValues))); + (mCurrentValue == null && other.mCurrentValue == null + && mCurrentValues != null && equalArrays(mCurrentValues, other.mCurrentValues))); } @Override public int hashCode() { int result = 17; - result = 31 * result + key.hashCode(); - if (currentValue != null) { - result = 31 * result + currentValue.hashCode(); - } else if (currentValues != null) { - for (String value : currentValues) { + result = 31 * result + mKey.hashCode(); + if (mCurrentValue != null) { + result = 31 * result + mCurrentValue.hashCode(); + } else if (mCurrentValues != null) { + for (String value : mCurrentValues) { if (value != null) { result = 31 * result + value.hashCode(); } @@ -359,14 +392,14 @@ public class RestrictionEntry implements Parcelable { } public RestrictionEntry(Parcel in) { - type = in.readInt(); - key = in.readString(); - title = in.readString(); - description = in.readString(); - choices = readArray(in); - values = readArray(in); - currentValue = in.readString(); - currentValues = readArray(in); + mType = in.readInt(); + mKey = in.readString(); + mTitle = in.readString(); + mDescription = in.readString(); + mChoiceEntries = readArray(in); + mChoiceValues = readArray(in); + mCurrentValue = in.readString(); + mCurrentValues = readArray(in); } @Override @@ -387,14 +420,14 @@ public class RestrictionEntry implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(type); - dest.writeString(key); - dest.writeString(title); - dest.writeString(description); - writeArray(dest, choices); - writeArray(dest, values); - dest.writeString(currentValue); - writeArray(dest, currentValues); + dest.writeInt(mType); + dest.writeString(mKey); + dest.writeString(mTitle); + dest.writeString(mDescription); + writeArray(dest, mChoiceEntries); + writeArray(dest, mChoiceValues); + dest.writeString(mCurrentValue); + writeArray(dest, mCurrentValues); } public static final Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() { @@ -409,6 +442,6 @@ public class RestrictionEntry implements Parcelable { @Override public String toString() { - return "RestrictionsEntry {type=" + type + ", key=" + key + ", value=" + currentValue + "}"; + return "RestrictionsEntry {type=" + mType + ", key=" + mKey + ", value=" + mCurrentValue + "}"; } } diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java new file mode 100644 index 0000000..0dd0edd --- /dev/null +++ b/core/java/android/content/RestrictionsManager.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.app.admin.DevicePolicyManager; +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + +/** + * Provides a mechanism for apps to query restrictions imposed by an entity that + * manages the user. Apps can also send permission requests to a local or remote + * device administrator to override default app-specific restrictions or any other + * operation that needs explicit authorization from the administrator. + * <p> + * Apps can expose a set of restrictions via a runtime receiver mechanism or via + * static meta data in the manifest. + * <p> + * If the user has an active restrictions provider, dynamic requests can be made in + * addition to the statically imposed restrictions. Dynamic requests are app-specific + * and can be expressed via a predefined set of templates. + * <p> + * The RestrictionsManager forwards the dynamic requests to the active + * restrictions provider. The restrictions provider can respond back to requests by calling + * {@link #notifyPermissionResponse(String, Bundle)}, when + * a response is received from the administrator of the device or user + * The response is relayed back to the application via a protected broadcast, + * {@link #ACTION_PERMISSION_RESPONSE_RECEIVED}. + * <p> + * Static restrictions are specified by an XML file referenced by a meta-data attribute + * in the manifest. This enables applications as well as any web administration consoles + * to be able to read the template from the apk. + * <p> + * The syntax of the XML format is as follows: + * <pre> + * <restrictions> + * <restriction + * android:key="<key>" + * android:restrictionType="boolean|string|integer|multi-select|null" + * ... /> + * <restriction ... /> + * </restrictions> + * </pre> + * <p> + * The attributes for each restriction depend on the restriction type. + * + * @see RestrictionEntry + */ +public class RestrictionsManager { + + /** + * Broadcast intent delivered when a response is received for a permission + * request. The response is not available for later query, so the receiver + * must persist and/or immediately act upon the response. The application + * should not interrupt the user by coming to the foreground if it isn't + * currently in the foreground. It can post a notification instead, informing + * the user of a change in state. + * <p> + * For instance, if the user requested permission to make an in-app purchase, + * the app can post a notification that the request had been granted or denied, + * and allow the purchase to go through. + * <p> + * The broadcast Intent carries the following extra: + * {@link #EXTRA_RESPONSE_BUNDLE}. + */ + public static final String ACTION_PERMISSION_RESPONSE_RECEIVED = + "android.intent.action.PERMISSION_RESPONSE_RECEIVED"; + + /** + * Protected broadcast intent sent to the active restrictions provider. The intent + * contains the following extras:<p> + * <ul> + * <li>{@link #EXTRA_PACKAGE_NAME} : String; the package name of the application requesting + * permission.</li> + * <li>{@link #EXTRA_TEMPLATE_ID} : String; the template of the request.</li> + * <li>{@link #EXTRA_REQUEST_BUNDLE} : Bundle; contains the template-specific keys and values + * for the request. + * </ul> + * @see DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName) + * @see #requestPermission(String, String, Bundle) + */ + public static final String ACTION_REQUEST_PERMISSION = + "android.intent.action.REQUEST_PERMISSION"; + + /** + * The package name of the application making the request. + */ + public static final String EXTRA_PACKAGE_NAME = "package_name"; + + /** + * The template id that specifies what kind of a request it is and may indicate + * how the request is to be presented to the administrator. Must be either one of + * the predefined templates or a custom one specified by the application that the + * restrictions provider is familiar with. + */ + public static final String EXTRA_TEMPLATE_ID = "template_id"; + + /** + * A bundle containing the details about the request. The contents depend on the + * template id. + * @see #EXTRA_TEMPLATE_ID + */ + public static final String EXTRA_REQUEST_BUNDLE = "request_bundle"; + + /** + * Contains a response from the administrator for specific request. + * The bundle contains the following information, at least: + * <ul> + * <li>{@link #REQUEST_KEY_ID}: The request id.</li> + * <li>{@link #REQUEST_KEY_DATA}: The request reference data.</li> + * </ul> + * <p> + * And depending on what the request template was, the bundle will contain the actual + * result of the request. For {@link #REQUEST_TEMPLATE_QUESTION}, the result will be in + * {@link #RESPONSE_KEY_BOOLEAN}, which is of type boolean; true if the administrator + * approved the request, false otherwise. + */ + public static final String EXTRA_RESPONSE_BUNDLE = "response_bundle"; + + + /** + * Request template that presents a simple question, with a possible title and icon. + * <p> + * Required keys are + * {@link #REQUEST_KEY_ID} and {@link #REQUEST_KEY_MESSAGE}. + * <p> + * Optional keys are + * {@link #REQUEST_KEY_DATA}, {@link #REQUEST_KEY_ICON}, {@link #REQUEST_KEY_TITLE}, + * {@link #REQUEST_KEY_APPROVE_LABEL} and {@link #REQUEST_KEY_DENY_LABEL}. + */ + public static final String REQUEST_TEMPLATE_QUESTION = "android.req_template.type.simple"; + + /** + * Key for request ID contained in the request bundle. + * <p> + * App-generated request id to identify the specific request when receiving + * a response. This value is returned in the {@link #EXTRA_RESPONSE_BUNDLE}. + * <p> + * Type: String + */ + public static final String REQUEST_KEY_ID = "android.req_template.req_id"; + + /** + * Key for request data contained in the request bundle. + * <p> + * Optional, typically used to identify the specific data that is being referred to, + * such as the unique identifier for a movie or book. This is not used for display + * purposes and is more like a cookie. This value is returned in the + * {@link #EXTRA_RESPONSE_BUNDLE}. + * <p> + * Type: String + */ + public static final String REQUEST_KEY_DATA = "android.req_template.data"; + + /** + * Key for request title contained in the request bundle. + * <p> + * Optional, typically used as the title of any notification or dialog presented + * to the administrator who approves the request. + * <p> + * Type: String + */ + public static final String REQUEST_KEY_TITLE = "android.req_template.title"; + + /** + * Key for request message contained in the request bundle. + * <p> + * Required, shown as the actual message in a notification or dialog presented + * to the administrator who approves the request. + * <p> + * Type: String + */ + public static final String REQUEST_KEY_MESSAGE = "android.req_template.mesg"; + + /** + * Key for request icon contained in the request bundle. + * <p> + * Optional, shown alongside the request message presented to the administrator + * who approves the request. + * <p> + * Type: Bitmap + */ + public static final String REQUEST_KEY_ICON = "android.req_template.icon"; + + /** + * Key for request approval button label contained in the request bundle. + * <p> + * Optional, may be shown as a label on the positive button in a dialog or + * notification presented to the administrator who approves the request. + * <p> + * Type: String + */ + public static final String REQUEST_KEY_APPROVE_LABEL = "android.req_template.accept"; + + /** + * Key for request rejection button label contained in the request bundle. + * <p> + * Optional, may be shown as a label on the negative button in a dialog or + * notification presented to the administrator who approves the request. + * <p> + * Type: String + */ + public static final String REQUEST_KEY_DENY_LABEL = "android.req_template.reject"; + + /** + * Key for requestor's name contained in the request bundle. This value is not specified by + * the application. It is automatically inserted into the Bundle by the Restrictions Provider + * before it is sent to the administrator. + * <p> + * Type: String + */ + public static final String REQUEST_KEY_REQUESTOR_NAME = "android.req_template.requestor"; + + /** + * Key for requestor's device name contained in the request bundle. This value is not specified + * by the application. It is automatically inserted into the Bundle by the Restrictions Provider + * before it is sent to the administrator. + * <p> + * Type: String + */ + public static final String REQUEST_KEY_DEVICE_NAME = "android.req_template.device"; + + /** + * Key for the response in the response bundle sent to the application, for a permission + * request. + * <p> + * Type: boolean + */ + public static final String RESPONSE_KEY_BOOLEAN = "android.req_template.response"; + + private static final String TAG = "RestrictionsManager"; + + private final Context mContext; + private final IRestrictionsManager mService; + + /** + * @hide + */ + public RestrictionsManager(Context context, IRestrictionsManager service) { + mContext = context; + mService = service; + } + + /** + * Returns any available set of application-specific restrictions applicable + * to this application. + * @return + */ + public Bundle getApplicationRestrictions() { + try { + if (mService != null) { + return mService.getApplicationRestrictions(mContext.getPackageName()); + } + } catch (RemoteException re) { + Log.w(TAG, "Couldn't reach service"); + } + return null; + } + + /** + * Called by an application to check if permission requests can be made. If false, + * there is no need to request permission for an operation, unless a static + * restriction applies to that operation. + * @return + */ + public boolean hasRestrictionsProvider() { + try { + if (mService != null) { + return mService.hasRestrictionsProvider(); + } + } catch (RemoteException re) { + Log.w(TAG, "Couldn't reach service"); + } + return false; + } + + /** + * Called by an application to request permission for an operation. The contents of the + * request are passed in a Bundle that contains several pieces of data depending on the + * chosen request template. + * + * @param requestTemplate The request template to use. The template could be one of the + * predefined templates specified in this class or a custom template that the specific + * Restrictions Provider might understand. For custom templates, the template name should be + * namespaced to avoid collisions with predefined templates and templates specified by + * other Restrictions Provider vendors. + * @param requestData A Bundle containing the data corresponding to the specified request + * template. The keys for the data in the bundle depend on the kind of template chosen. + */ + public void requestPermission(String requestTemplate, Bundle requestData) { + try { + if (mService != null) { + mService.requestPermission(mContext.getPackageName(), requestTemplate, requestData); + } + } catch (RemoteException re) { + Log.w(TAG, "Couldn't reach service"); + } + } + + /** + * Called by the Restrictions Provider when a response is available to be + * delivered to an application. + * @param packageName + * @param response + */ + public void notifyPermissionResponse(String packageName, Bundle response) { + try { + if (mService != null) { + mService.notifyPermissionResponse(packageName, response); + } + } catch (RemoteException re) { + Log.w(TAG, "Couldn't reach service"); + } + } + + /** + * Parse and return the list of restrictions defined in the manifest for the specified + * package, if any. + * @param packageName The application for which to fetch the restrictions list. + * @return The list of RestrictionEntry objects created from the XML file specified + * in the manifest, or null if none was specified. + */ + public List<RestrictionEntry> getManifestRestrictions(String packageName) { + // TODO: + return null; + } +} diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java index 6b4404d..46c9234 100644 --- a/core/java/android/content/SharedPreferences.java +++ b/core/java/android/content/SharedPreferences.java @@ -355,7 +355,14 @@ public interface SharedPreferences { /** * Registers a callback to be invoked when a change happens to a preference. - * + * + * <p class="caution"><strong>Caution:</strong> The preference manager does + * not currently store a strong reference to the listener. You must store a + * strong reference to the listener, or it will be susceptible to garbage + * collection. We recommend you keep a reference to the listener in the + * instance data of an object that will exist as long as you need the + * listener.</p> + * * @param listener The callback that will run. * @see #unregisterOnSharedPreferenceChangeListener */ diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 6cb781f..70668e1 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -112,7 +112,7 @@ interface IPackageManager { ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId); - boolean canForwardTo(in Intent intent, String resolvedType, int userIdFrom, int userIdDest); + boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId); List<ResolveInfo> queryIntentActivities(in Intent intent, String resolvedType, int flags, int userId); @@ -248,10 +248,10 @@ interface IPackageManager { void clearPackagePersistentPreferredActivities(String packageName, int userId); - void addForwardingIntentFilter(in IntentFilter filter, boolean removable, int userIdOrig, - int userIdDest); + void addCrossProfileIntentFilter(in IntentFilter filter, boolean removable, int sourceUserId, + int targetUserId); - void clearForwardingIntentFilters(int userIdOrig); + void clearCrossProfileIntentFilters(int sourceUserId); /** * Report the set of 'Home' activity candidates, plus (if any) which of them @@ -433,6 +433,13 @@ interface IPackageManager { in VerificationParams verificationParams, in ContainerEncryptionParams encryptionParams); + void installPackageWithVerificationEncryptionAndAbiOverrideEtc(in Uri packageURI, + in IPackageInstallObserver observer, in IPackageInstallObserver2 observer2, + int flags, in String installerPackageName, + in VerificationParams verificationParams, + in ContainerEncryptionParams encryptionParams, + in String packageAbiOverride); + int installExistingPackageAsUser(String packageName, int userId); void verifyPendingInstall(int id, int verificationCode); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index d7bd473..4672015 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -19,9 +19,12 @@ package android.content.pm; import android.app.PackageInstallObserver; import android.app.PackageUninstallObserver; import android.content.pm.PackageManager.NameNotFoundException; +import android.os.FileBridge; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import java.io.OutputStream; + /** {@hide} */ public class PackageInstaller { private final PackageManager mPm; @@ -127,10 +130,17 @@ public class PackageInstaller { } } - public ParcelFileDescriptor openWrite(String overlayName, long offsetBytes, - long lengthBytes) { + /** + * Open an APK file for writing, starting at the given offset. You can + * then stream data into the file, periodically calling + * {@link OutputStream#flush()} to ensure bytes have been written to + * disk. + */ + public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) { try { - return mSession.openWrite(overlayName, offsetBytes, lengthBytes); + final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName, + offsetBytes, lengthBytes); + return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor()); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 35bcc02..c5cd5c9 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3576,24 +3576,38 @@ public abstract class PackageManager { } /** - * Adds a forwarding intent filter. After calling this method all intents sent from the user - * with id userIdOrig can also be be resolved by activities in the user with id userIdDest if - * they match the specified intent filter. - * @param filter the {@link IntentFilter} the intent has to match to be forwarded - * @param removable if set to false, {@link clearForwardingIntents} will not remove this intent - * filter - * @param userIdOrig user from which the intent can be forwarded - * @param userIdDest user to which the intent can be forwarded + * 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 + * targetUserId if they match the specified intent filter. + * @param filter the {@link IntentFilter} the intent has to match + * @param removable if set to false, {@link clearCrossProfileIntentFilters} will not remove this + * {@link CrossProfileIntentFilter} * @hide */ + public abstract void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, + int sourceUserId, int targetUserId); + + /** + * @hide + * @deprecated + * TODO: remove it as soon as the code of ManagedProvisionning is updated + */ public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable, - int userIdOrig, int userIdDest); + int sourceUserId, int targetUserId); /** - * Clearing all removable {@link ForwardingIntentFilter}s that are set with the given user as - * the origin. - * @param userIdOrig user from which the intent can be forwarded + * Clearing removable {@link CrossProfileIntentFilter}s which have the specified user as their + * source + * @param sourceUserId + * be cleared. * @hide */ - public abstract void clearForwardingIntentFilters(int userIdOrig); + public abstract void clearCrossProfileIntentFilters(int sourceUserId); + + /** + * @hide + * @deprecated + * TODO: remove it as soon as the code of ManagedProvisionning is updated + */ + public abstract void clearForwardingIntentFilters(int sourceUserId); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 1c838c3..ab8bf61 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2174,7 +2174,6 @@ public class PackageParser { } final int innerDepth = parser.getDepth(); - int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { @@ -2548,13 +2547,13 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_singleUser, false)) { a.info.flags |= ActivityInfo.FLAG_SINGLE_USER; - if (a.info.exported) { + if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Activity exported request ignored due to singleUser: " + a.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); a.info.exported = false; + setExported = true; } - setExported = true; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_primaryUserOnly, @@ -2907,7 +2906,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestProvider_singleUser, false)) { p.info.flags |= ProviderInfo.FLAG_SINGLE_USER; - if (p.info.exported) { + if (p.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Provider exported request ignored due to singleUser: " + p.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); @@ -3181,13 +3180,13 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestService_singleUser, false)) { s.info.flags |= ServiceInfo.FLAG_SINGLE_USER; - if (s.info.exported) { + if (s.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Service exported request ignored due to singleUser: " + s.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); s.info.exported = false; + setExported = true; } - setExported = true; } sa.recycle(); diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 86208fc..c8de2f1 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -142,9 +142,10 @@ public final class Sensor { public static final String STRING_TYPE_TEMPERATURE = "android.sensor.temperature"; /** - * A constant describing a proximity sensor type. + * A constant describing a proximity sensor type. This is a wake up sensor. * <p>See {@link android.hardware.SensorEvent#values SensorEvent.values} * for more details. + * @see #isWakeUpSensor() */ public static final int TYPE_PROXIMITY = 8; @@ -307,8 +308,10 @@ public final class Sensor { * itself. The sensor continues to operate while the device is asleep * and will automatically wake the device to notify when significant * motion is detected. The application does not need to hold any wake - * locks for this sensor to trigger. + * locks for this sensor to trigger. This is a wake up sensor. * <p>See {@link TriggerEvent} for more details. + * + * @see #isWakeUpSensor() */ public static final int TYPE_SIGNIFICANT_MOTION = 17; @@ -381,11 +384,17 @@ public final class Sensor { /** * A constant describing a heart rate monitor. * <p> - * A sensor that measures the heart rate in beats per minute. + * The reported value is the heart rate in beats per minute. + * <p> + * The reported accuracy represents the status of the monitor during the reading. See the + * {@code SENSOR_STATUS_*} constants in {@link android.hardware.SensorManager SensorManager} + * for more details on accuracy/status values. In particular, when the accuracy is + * {@code SENSOR_STATUS_UNRELIABLE} or {@code SENSOR_STATUS_NO_CONTACT}, the heart rate + * value should be discarded. * <p> - * value[0] represents the beats per minute when the measurement was taken. - * value[0] is 0 if the heart rate monitor could not measure the rate or the - * rate is 0 beat per minute. + * This sensor requires permission {@code android.permission.BODY_SENSORS}. + * It will not be returned by {@code SensorManager.getSensorsList} nor + * {@code SensorManager.getDefaultSensor} if the application doesn't have this permission. */ public static final int TYPE_HEART_RATE = 21; @@ -397,6 +406,321 @@ public final class Sensor { public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; /** + * A non-wake up variant of proximity sensor. + * + * @see #TYPE_PROXIMITY + */ + public static final int TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = 22; + + /** + * A constant string describing a non-wake up proximity sensor type. + * + * @see #TYPE_NON_WAKE_UP_PROXIMITY_SENSOR + */ + public static final String SENSOR_STRING_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = + "android.sensor.non_wake_up_proximity_sensor"; + + /** + * A constant describing a wake up variant of accelerometer sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_ACCELEROMETER + */ + public static final int TYPE_WAKE_UP_ACCELEROMETER = 23; + + /** + * A constant string describing a wake up accelerometer. + * + * @see #TYPE_WAKE_UP_ACCELEROMETER + */ + public static final String STRING_TYPE_WAKE_UP_ACCELEROMETER = + "android.sensor.wake_up_accelerometer"; + + /** + * A constant describing a wake up variant of a magnetic field sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_MAGNETIC_FIELD + */ + public static final int TYPE_WAKE_UP_MAGNETIC_FIELD = 24; + + /** + * A constant string describing a wake up magnetic field sensor. + * + * @see #TYPE_WAKE_UP_MAGNETIC_FIELD + */ + public static final String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD = + "android.sensor.wake_up_magnetic_field"; + + /** + * A constant describing a wake up variant of an orientation sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_ORIENTATION + */ + public static final int TYPE_WAKE_UP_ORIENTATION = 25; + + /** + * A constant string describing a wake up orientation sensor. + * + * @see #TYPE_WAKE_UP_ORIENTATION + */ + public static final String STRING_TYPE_WAKE_UP_ORIENTATION = + "android.sensor.wake_up_orientation"; + + /** + * A constant describing a wake up variant of a gyroscope sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GYROSCOPE + */ + public static final int TYPE_WAKE_UP_GYROSCOPE = 26; + + /** + * A constant string describing a wake up gyroscope sensor type. + * + * @see #TYPE_WAKE_UP_GYROSCOPE + */ + public static final String STRING_TYPE_WAKE_UP_GYROSCOPE = "android.sensor.wake_up_gyroscope"; + + /** + * A constant describing a wake up variant of a light sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_LIGHT + */ + public static final int TYPE_WAKE_UP_LIGHT = 27; + + /** + * A constant string describing a wake up light sensor type. + * + * @see #TYPE_WAKE_UP_LIGHT + */ + public static final String STRING_TYPE_WAKE_UP_LIGHT = "android.sensor.wake_up_light"; + + /** + * A constant describing a wake up variant of a pressure sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_PRESSURE + */ + public static final int TYPE_WAKE_UP_PRESSURE = 28; + + /** + * A constant string describing a wake up pressure sensor type. + * + * @see #TYPE_WAKE_UP_PRESSURE + */ + public static final String STRING_TYPE_WAKE_UP_PRESSURE = "android.sensor.wake_up_pressure"; + + /** + * A constant describing a wake up variant of a gravity sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GRAVITY + */ + public static final int TYPE_WAKE_UP_GRAVITY = 29; + + /** + * A constant string describing a wake up gravity sensor type. + * + * @see #TYPE_WAKE_UP_GRAVITY + */ + public static final String STRING_TYPE_WAKE_UP_GRAVITY = "android.sensor.wake_up_gravity"; + + /** + * A constant describing a wake up variant of a linear acceleration sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_LINEAR_ACCELERATION + */ + public static final int TYPE_WAKE_UP_LINEAR_ACCELERATION = 30; + + /** + * A constant string describing a wake up linear acceleration sensor type. + * + * @see #TYPE_WAKE_UP_LINEAR_ACCELERATION + */ + public static final String STRING_TYPE_WAKE_UP_LINEAR_ACCELERATION = + "android.sensor.wake_up_linear_acceleration"; + + /** + * A constant describing a wake up variant of a rotation vector sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_ROTATION_VECTOR + */ + public static final int TYPE_WAKE_UP_ROTATION_VECTOR = 31; + + /** + * A constant string describing a wake up rotation vector sensor type. + * + * @see #TYPE_WAKE_UP_ROTATION_VECTOR + */ + public static final String STRING_TYPE_WAKE_UP_ROTATION_VECTOR = + "android.sensor.wake_up_rotation_vector"; + + /** + * A constant describing a wake up variant of a relative humidity sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_RELATIVE_HUMIDITY + */ + public static final int TYPE_WAKE_UP_RELATIVE_HUMIDITY = 32; + + /** + * A constant string describing a wake up relative humidity sensor type. + * + * @see #TYPE_WAKE_UP_RELATIVE_HUMIDITY + */ + public static final String STRING_TYPE_WAKE_UP_RELATIVE_HUMIDITY = + "android.sensor.wake_up_relative_humidity"; + + /** + * A constant describing a wake up variant of an ambient temperature sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_AMBIENT_TEMPERATURE + */ + public static final int TYPE_WAKE_UP_AMBIENT_TEMPERATURE = 33; + + /** + * A constant string describing a wake up ambient temperature sensor type. + * + * @see #TYPE_WAKE_UP_AMBIENT_TEMPERATURE + */ + public static final String STRING_TYPE_WAKE_UP_AMBIENT_TEMPERATURE = + "android.sensor.wake_up_ambient_temperature"; + + /** + * A constant describing a wake up variant of an uncalibrated magnetic field sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_MAGNETIC_FIELD_UNCALIBRATED + */ + public static final int TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = 34; + + /** + * A constant string describing a wake up uncalibrated magnetic field sensor type. + * + * @see #TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED + */ + public static final String STRING_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED = + "android.sensor.wake_up_magnetic_field_uncalibrated"; + + /** + * A constant describing a wake up variant of a game rotation vector sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GAME_ROTATION_VECTOR + */ + public static final int TYPE_WAKE_UP_GAME_ROTATION_VECTOR = 35; + + /** + * A constant string describing a wake up game rotation vector sensor type. + * + * @see #TYPE_WAKE_UP_GAME_ROTATION_VECTOR + */ + public static final String STRING_TYPE_WAKE_UP_GAME_ROTATION_VECTOR = + "android.sensor.wake_up_game_rotation_vector"; + + /** + * A constant describing a wake up variant of an uncalibrated gyroscope sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GYROSCOPE_UNCALIBRATED + */ + public static final int TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = 36; + + /** + * A constant string describing a wake up uncalibrated gyroscope sensor type. + * + * @see #TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED + */ + public static final String STRING_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED = + "android.sensor.wake_up_gyroscope_uncalibrated"; + + /** + * A constant describing a wake up variant of a step detector sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_STEP_DETECTOR + */ + public static final int TYPE_WAKE_UP_STEP_DETECTOR = 37; + + /** + * A constant string describing a wake up step detector sensor type. + * + * @see #TYPE_WAKE_UP_STEP_DETECTOR + */ + public static final String STRING_TYPE_WAKE_UP_STEP_DETECTOR = + "android.sensor.wake_up_step_detector"; + + /** + * A constant describing a wake up variant of a step counter sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_STEP_COUNTER + */ + public static final int TYPE_WAKE_UP_STEP_COUNTER = 38; + + /** + * A constant string describing a wake up step counter sensor type. + * + * @see #TYPE_WAKE_UP_STEP_COUNTER + */ + public static final String STRING_TYPE_WAKE_UP_STEP_COUNTER = + "android.sensor.wake_up_step_counter"; + + /** + * A constant describing a wake up variant of a geomagnetic rotation vector sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_GEOMAGNETIC_ROTATION_VECTOR + */ + public static final int TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = 39; + + /** + * A constant string describing a wake up geomagnetic rotation vector sensor type. + * + * @see #TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR + */ + public static final String STRING_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR = + "android.sensor.wake_up_geomagnetic_rotation_vector"; + + /** + * A constant describing a wake up variant of a heart rate sensor type. + * + * @see #isWakeUpSensor() + * @see #TYPE_HEART_RATE + */ + public static final int TYPE_WAKE_UP_HEART_RATE = 40; + + /** + * A constant string describing a wake up heart rate sensor type. + * + * @see #TYPE_WAKE_UP_HEART_RATE + */ + public static final String STRING_TYPE_WAKE_UP_HEART_RATE = "android.sensor.wake_up_heart_rate"; + + /** + * A sensor of this type generates an event each time a tilt event is detected. A tilt event + * is generated if the direction of the 2-seconds window average gravity changed by at + * least 35 degrees since the activation of the sensor. It is a wake up sensor. + * + * @see #isWakeUpSensor() + */ + public static final int TYPE_WAKE_UP_TILT_DETECTOR = 41; + + /** + * A constant string describing a wake up tilt detector sensor type. + * + * @see #TYPE_WAKE_UP_TILT_DETECTOR + */ + public static final String SENSOR_STRING_TYPE_WAKE_UP_TILT_DETECTOR = + "android.sensor.wake_up_tilt_detector"; + + /** * A constant describing a wake gesture sensor. * <p> * Wake gesture sensors enable waking up the device based on a device specific motion. @@ -410,6 +734,7 @@ public final class Sensor { * the device. This sensor must be low power, as it is likely to be activated 24/7. * Values of events created by this sensors should not be used. * + * @see #isWakeUpSensor() * @hide This sensor is expected to only be used by the power manager */ public static final int TYPE_WAKE_GESTURE = 42; @@ -467,7 +792,29 @@ public final class Sensor { REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_DETECTOR REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_COUNTER REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR - REPORTING_MODE_ON_CHANGE, 1 // SENSOR_TYPE_HEART_RATE_MONITOR + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_HEART_RATE_MONITOR + REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR + // wake up variants of base sensors + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_ACCELEROMETER + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_ORIENTATION + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_GYROSCOPE + REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_LIGHT + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_PRESSURE + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_GRAVITY + REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_LINEAR_ACCELERATION + REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_WAKE_UP_ROTATION_VECTOR + REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_RELATIVE_HUMIDITY + REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_AMBIENT_TEMPERATURE + REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED + REPORTING_MODE_CONTINUOUS, 4, // SENSOR_TYPE_WAKE_UP_GAME_ROTATION_VECTOR + REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_STEP_DETECTOR + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_STEP_COUNTER + REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_HEART_RATE_MONITOR + REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_TILT_DETECTOR + REPORTING_MODE_ONE_SHOT, 1, // SENSOR_TYPE_WAKE_GESTURE }; static int getReportingMode(Sensor sensor) { @@ -525,6 +872,8 @@ public final class Sensor { private int mFifoMaxEventCount; private String mStringType; private String mRequiredPermission; + private int mMaxDelay; + private boolean mWakeUpSensor; Sensor() { } @@ -613,6 +962,7 @@ public final class Sensor { } /** + * @hide * @return The permission required to access this sensor. If empty, no permission is required. */ public String getRequiredPermission() { @@ -624,6 +974,51 @@ public final class Sensor { return mHandle; } + /** + * This value is defined only for continuous mode sensors. It is the delay between two + * sensor events corresponding to the lowest frequency that this sensor supports. When + * lower frequencies are requested through registerListener() the events will be generated + * at this frequency instead. It can be used to estimate when the batch FIFO may be full. + * Older devices may set this value to zero. Ignore this value in case it is negative + * or zero. + * + * @return The max delay for this sensor in microseconds. + */ + public int getMaxDelay() { + return mMaxDelay; + } + + /** + * Returns whether this sensor is a wake-up sensor. + * <p> + * Wake up sensors wake the application processor up when they have events to deliver. When a + * wake up sensor is registered to without batching enabled, each event will wake the + * application processor up. + * <p> + * When a wake up sensor is registered to with batching enabled, it + * wakes the application processor up when maxReportingLatency has elapsed or when the hardware + * FIFO storing the events from wake up sensors is getting full. + * <p> + * Non-wake up sensors never wake the application processor up. Their events are only reported + * when the application processor is awake, for example because the application holds a wake + * lock, or another source woke the application processor up. + * <p> + * When a non-wake up sensor is registered to without batching enabled, the measurements made + * while the application processor is asleep might be lost and never returned. + * <p> + * When a non-wake up sensor is registered to with batching enabled, the measurements made while + * the application processor is asleep are stored in the hardware FIFO for non-wake up sensors. + * When this FIFO gets full, new events start overwriting older events. When the application + * then wakes up, the latest events are returned, and some old events might be lost. The number + * of events actually returned depends on the hardware FIFO size, as well as on what other + * sensors are activated. If losing sensor events is not acceptable during batching, you must + * use the wake-up version of the sensor. + * @return true if this is a wake up sensor, false otherwise. + */ + public boolean isWakeUpSensor() { + return mWakeUpSensor; + } + void setRange(float max, float res) { mMaxRange = max; mResolution = res; diff --git a/core/java/android/hardware/SensorEventListener.java b/core/java/android/hardware/SensorEventListener.java index 677d244..0d859fb 100644 --- a/core/java/android/hardware/SensorEventListener.java +++ b/core/java/android/hardware/SensorEventListener.java @@ -39,11 +39,13 @@ public interface SensorEventListener { public void onSensorChanged(SensorEvent event); /** - * Called when the accuracy of a sensor has changed. - * <p>See {@link android.hardware.SensorManager SensorManager} - * for details. + * Called when the accuracy of the registered sensor has changed. + * + * <p>See the SENSOR_STATUS_* constants in + * {@link android.hardware.SensorManager SensorManager} for details. * - * @param accuracy The new accuracy of this sensor + * @param accuracy The new accuracy of this sensor, one of + * {@code SensorManager.SENSOR_STATUS_*} */ - public void onAccuracyChanged(Sensor sensor, int accuracy); + public void onAccuracyChanged(Sensor sensor, int accuracy); } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 5f2b5f0..25c7630 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -321,6 +321,13 @@ public abstract class SensorManager { /** + * The values returned by this sensor cannot be trusted because the sensor + * had no contact with what it was measuring (for example, the heart rate + * monitor is not in contact with the user). + */ + public static final int SENSOR_STATUS_NO_CONTACT = -1; + + /** * The values returned by this sensor cannot be trusted, calibration is * needed or the environment doesn't allow readings */ @@ -421,9 +428,10 @@ public abstract class SensorManager { * {@link SensorManager#getSensorList(int) getSensorList}. * * @param type - * of sensors requested + * of sensors requested * - * @return the default sensors matching the asked type. + * @return the default sensor matching the requested type if one exists and the application + * has the necessary permissions, or null otherwise. * * @see #getSensorList(int) * @see Sensor diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 8684a04..b66ec86 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -395,25 +395,12 @@ public class SystemSensorManager extends SensorManager { t.timestamp = timestamp; t.accuracy = inAccuracy; t.sensor = sensor; - switch (t.sensor.getType()) { - // Only report accuracy for sensors that support it. - case Sensor.TYPE_MAGNETIC_FIELD: - case Sensor.TYPE_ORIENTATION: - // call onAccuracyChanged() only if the value changes - final int accuracy = mSensorAccuracies.get(handle); - if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { - mSensorAccuracies.put(handle, t.accuracy); - mListener.onAccuracyChanged(t.sensor, t.accuracy); - } - break; - default: - // For other sensors, just report the accuracy once - if (mFirstEvent.get(handle) == false) { - mFirstEvent.put(handle, true); - mListener.onAccuracyChanged( - t.sensor, SENSOR_STATUS_ACCURACY_HIGH); - } - break; + + // call onAccuracyChanged() only if the value changes + final int accuracy = mSensorAccuracies.get(handle); + if ((t.accuracy >= 0) && (accuracy != t.accuracy)) { + mSensorAccuracies.put(handle, t.accuracy); + mListener.onAccuracyChanged(t.sensor, t.accuracy); } mListener.onSensorChanged(t); } diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java index 1af575f..ca71e81 100644 --- a/core/java/android/hardware/camera2/CameraAccessException.java +++ b/core/java/android/hardware/camera2/CameraAccessException.java @@ -114,7 +114,10 @@ public class CameraAccessException extends AndroidException { mReason = problem; } - private static String getDefaultMessage(int problem) { + /** + * @hide + */ + public static String getDefaultMessage(int problem) { switch (problem) { case CAMERA_IN_USE: return "The camera device is in use already"; diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 83db056..4a89fe7 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -84,9 +84,8 @@ public final class CameraManager { try { CameraBinderDecorator.throwOnError( CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor()); - } catch(CameraRuntimeException e) { - throw new IllegalStateException("Failed to setup camera vendor tags", - e.asChecked()); + } catch (CameraRuntimeException e) { + handleRecoverableSetupErrors(e, "Failed to set up vendor tags"); } try { @@ -426,6 +425,18 @@ public final class CameraManager { return mDeviceIdList; } + private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) { + int problem = e.getReason(); + switch (problem) { + case CameraAccessException.CAMERA_DISCONNECTED: + String errorMsg = CameraAccessException.getDefaultMessage(problem); + Log.w(TAG, msg + ": " + errorMsg); + break; + default: + throw new IllegalStateException(msg, e.asChecked()); + } + } + // TODO: this class needs unit tests // TODO: extract class into top level private class CameraServiceListener extends ICameraServiceListener.Stub { diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java index a71a74d..723eda1 100644 --- a/core/java/android/hardware/hdmi/HdmiCec.java +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -194,6 +194,8 @@ public final class HdmiCec { DEVICE_RECORDER, // ADDR_RECORDER_3 DEVICE_TUNER, // ADDR_TUNER_4 DEVICE_PLAYBACK, // ADDR_PLAYBACK_3 + DEVICE_RESERVED, + DEVICE_RESERVED, DEVICE_TV, // ADDR_SPECIFIC_USE }; @@ -210,6 +212,8 @@ public final class HdmiCec { "Recorder_3", "Tuner_4", "Playback_3", + "Reserved_1", + "Reserved_2", "Secondary_TV", }; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 2f2aba3..a48a388 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -40,6 +40,7 @@ import android.util.ArrayMap; import android.util.Log; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.Protocol; import java.net.InetAddress; @@ -807,11 +808,34 @@ public class ConnectivityManager { * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api. */ public int startUsingNetworkFeature(int networkType, String feature) { - try { - return mService.startUsingNetworkFeature(networkType, feature, - new Binder()); - } catch (RemoteException e) { - return -1; + NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature); + if (netCap == null) { + Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " + + feature); + return PhoneConstants.APN_REQUEST_FAILED; + } + + NetworkRequest request = null; + synchronized (sLegacyRequests) { + LegacyRequest l = sLegacyRequests.get(netCap); + if (l != null) { + Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest); + renewRequestLocked(l); + if (l.currentNetwork != null) { + return PhoneConstants.APN_ALREADY_ACTIVE; + } else { + return PhoneConstants.APN_REQUEST_STARTED; + } + } + + request = requestNetworkForFeatureLocked(netCap); + } + if (request != null) { + Log.d(TAG, "starting startUsingNeworkFeature for request " + request); + return PhoneConstants.APN_REQUEST_STARTED; + } else { + Log.d(TAG, " request Failed"); + return PhoneConstants.APN_REQUEST_FAILED; } } @@ -831,11 +855,172 @@ public class ConnectivityManager { * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api. */ public int stopUsingNetworkFeature(int networkType, String feature) { - try { - return mService.stopUsingNetworkFeature(networkType, feature); - } catch (RemoteException e) { + NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature); + if (netCap == null) { + Log.d(TAG, "Can't satisfy stopUsingNetworkFeature for " + networkType + ", " + + feature); return -1; } + + NetworkRequest request = removeRequestForFeature(netCap); + if (request != null) { + Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature); + releaseNetworkRequest(request); + } + return 1; + } + + private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) { + if (networkType == TYPE_MOBILE) { + int cap = -1; + if ("enableMMS".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_MMS; + } else if ("enableSUPL".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_SUPL; + } else if ("enableDUN".equals(feature) || "enableDUNAlways".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_DUN; + } else if ("enableHIPRI".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_INTERNET; + } else if ("enableFOTA".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_FOTA; + } else if ("enableIMS".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_IMS; + } else if ("enableCBS".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_CBS; + } else { + return null; + } + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + netCap.addNetworkCapability(cap); + return netCap; + } else if (networkType == TYPE_WIFI) { + if ("p2p".equals(feature)) { + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P); + return netCap; + } + } + return null; + } + + private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) { + if (netCap == null) return TYPE_NONE; + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) { + return TYPE_MOBILE_CBS; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) { + return TYPE_MOBILE_IMS; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) { + return TYPE_MOBILE_FOTA; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) { + return TYPE_MOBILE_DUN; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) { + return TYPE_MOBILE_SUPL; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) { + return TYPE_MOBILE_MMS; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + return TYPE_MOBILE_HIPRI; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P)) { + return TYPE_WIFI_P2P; + } + return TYPE_NONE; + } + + private static class LegacyRequest { + NetworkCapabilities networkCapabilities; + NetworkRequest networkRequest; + int expireSequenceNumber; + Network currentNetwork; + int delay = -1; + NetworkCallbackListener networkCallbackListener = new NetworkCallbackListener() { + @Override + public void onAvailable(NetworkRequest request, Network network) { + currentNetwork = network; + Log.d(TAG, "startUsingNetworkFeature got Network:" + network); + network.bindProcessForHostResolution(); + } + @Override + public void onLost(NetworkRequest request, Network network) { + if (network.equals(currentNetwork)) { + currentNetwork = null; + network.unbindProcessForHostResolution(); + } + Log.d(TAG, "startUsingNetworkFeature lost Network:" + network); + } + }; + } + + private HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests = + new HashMap<NetworkCapabilities, LegacyRequest>(); + + private NetworkRequest findRequestForFeature(NetworkCapabilities netCap) { + synchronized (sLegacyRequests) { + LegacyRequest l = sLegacyRequests.get(netCap); + if (l != null) return l.networkRequest; + } + return null; + } + + private void renewRequestLocked(LegacyRequest l) { + l.expireSequenceNumber++; + Log.d(TAG, "renewing request to seqNum " + l.expireSequenceNumber); + sendExpireMsgForFeature(l.networkCapabilities, l.expireSequenceNumber, l.delay); + } + + private void expireRequest(NetworkCapabilities netCap, int sequenceNum) { + int ourSeqNum = -1; + synchronized (sLegacyRequests) { + LegacyRequest l = sLegacyRequests.get(netCap); + if (l == null) return; + ourSeqNum = l.expireSequenceNumber; + if (l.expireSequenceNumber == sequenceNum) { + releaseNetworkRequest(l.networkRequest); + sLegacyRequests.remove(netCap); + } + } + Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum); + } + + private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) { + int delay = -1; + int type = legacyTypeForNetworkCapabilities(netCap); + try { + delay = mService.getRestoreDefaultNetworkDelay(type); + } catch (RemoteException e) {} + LegacyRequest l = new LegacyRequest(); + l.networkCapabilities = netCap; + l.delay = delay; + l.expireSequenceNumber = 0; + l.networkRequest = sendRequestForNetwork(netCap, l.networkCallbackListener, 0, + REQUEST, type); + if (l.networkRequest == null) return null; + sLegacyRequests.put(netCap, l); + sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay); + return l.networkRequest; + } + + private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) { + if (delay >= 0) { + Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay); + Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap); + sCallbackHandler.sendMessageDelayed(msg, delay); + } + } + + private NetworkRequest removeRequestForFeature(NetworkCapabilities netCap) { + synchronized (sLegacyRequests) { + LegacyRequest l = sLegacyRequests.remove(netCap); + if (l == null) return null; + return l.networkRequest; + } } /** @@ -1782,8 +1967,10 @@ public class ConnectivityManager { public static final int CALLBACK_RELEASED = BASE + 8; /** @hide */ public static final int CALLBACK_EXIT = BASE + 9; + /** @hide obj = NetworkCapabilities, arg1 = seq number */ + private static final int EXPIRE_LEGACY_REQUEST = BASE + 10; - private static class CallbackHandler extends Handler { + private class CallbackHandler extends Handler { private final HashMap<NetworkRequest, NetworkCallbackListener>mCallbackMap; private final AtomicInteger mRefCount; private static final String TAG = "ConnectivityManager.CallbackHandler"; @@ -1903,6 +2090,10 @@ public class ConnectivityManager { getLooper().quit(); break; } + case EXPIRE_LEGACY_REQUEST: { + expireRequest((NetworkCapabilities)message.obj, message.arg1); + break; + } } } @@ -1954,8 +2145,9 @@ public class ConnectivityManager { private final static int LISTEN = 1; private final static int REQUEST = 2; - private NetworkRequest somethingForNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener, int timeoutSec, int action) { + private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, + NetworkCallbackListener networkCallbackListener, int timeoutSec, int action, + int legacyType) { NetworkRequest networkRequest = null; if (networkCallbackListener == null) { throw new IllegalArgumentException("null NetworkCallbackListener"); @@ -1968,7 +2160,7 @@ public class ConnectivityManager { new Binder()); } else { networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler), - timeoutSec, new Binder()); + timeoutSec, new Binder(), legacyType); } if (networkRequest != null) { synchronized(sNetworkCallbackListener) { @@ -1998,7 +2190,7 @@ public class ConnectivityManager { */ public NetworkRequest requestNetwork(NetworkCapabilities need, NetworkCallbackListener networkCallbackListener) { - return somethingForNetwork(need, networkCallbackListener, 0, REQUEST); + return sendRequestForNetwork(need, networkCallbackListener, 0, REQUEST, TYPE_NONE); } /** @@ -2021,7 +2213,8 @@ public class ConnectivityManager { */ public NetworkRequest requestNetwork(NetworkCapabilities need, NetworkCallbackListener networkCallbackListener, int timeoutSec) { - return somethingForNetwork(need, networkCallbackListener, timeoutSec, REQUEST); + return sendRequestForNetwork(need, networkCallbackListener, timeoutSec, REQUEST, + TYPE_NONE); } /** @@ -2099,7 +2292,7 @@ public class ConnectivityManager { */ public NetworkRequest listenForNetwork(NetworkCapabilities need, NetworkCallbackListener networkCallbackListener) { - return somethingForNetwork(need, networkCallbackListener, 0, LISTEN); + return sendRequestForNetwork(need, networkCallbackListener, 0, LISTEN, TYPE_NONE); } /** diff --git a/core/java/android/net/ConnectivityServiceProtocol.java b/core/java/android/net/ConnectivityServiceProtocol.java deleted file mode 100644 index 74096b4..0000000 --- a/core/java/android/net/ConnectivityServiceProtocol.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import static com.android.internal.util.Protocol.BASE_CONNECTIVITY_SERVICE; - -/** - * Describes the Internal protocols used to communicate with ConnectivityService. - * @hide - */ -public class ConnectivityServiceProtocol { - - private static final int BASE = BASE_CONNECTIVITY_SERVICE; - - private ConnectivityServiceProtocol() {} - - /** - * This is a contract between ConnectivityService and various bearers. - * A NetworkFactory is an abstract entity that creates NetworkAgent objects. - * The bearers register with ConnectivityService using - * ConnectivityManager.registerNetworkFactory, where they pass in a Messenger - * to be used to deliver the following Messages. - */ - public static class NetworkFactoryProtocol { - private NetworkFactoryProtocol() {} - /** - * Pass a network request to the bearer. If the bearer believes it can - * satisfy the request it should connect to the network and create a - * NetworkAgent. Once the NetworkAgent is fully functional it will - * register itself with ConnectivityService using registerNetworkAgent. - * If the bearer cannot immediately satisfy the request (no network, - * user disabled the radio, lower-scored network) it should remember - * any NetworkRequests it may be able to satisfy in the future. It may - * disregard any that it will never be able to service, for example - * those requiring a different bearer. - * msg.obj = NetworkRequest - * msg.arg1 = score - the score of the any network currently satisfying this - * request. If this bearer knows in advance it cannot - * exceed this score it should not try to connect, holding the request - * for the future. - * Note that subsequent events may give a different (lower - * or higher) score for this request, transmitted to each - * NetworkFactory through additional CMD_REQUEST_NETWORK msgs - * with the same NetworkRequest but an updated score. - * Also, network conditions may change for this bearer - * allowing for a better score in the future. - */ - public static final int CMD_REQUEST_NETWORK = BASE; - - /** - * Cancel a network request - * msg.obj = NetworkRequest - */ - public static final int CMD_CANCEL_REQUEST = BASE + 1; - } -} diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index baec36a..5f1ff3e 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -158,7 +158,7 @@ interface IConnectivityManager in NetworkCapabilities nc, int score); NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, int timeoutSec, in IBinder binder); + in Messenger messenger, int timeoutSec, in IBinder binder, int legacy); NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities, in PendingIntent operation); @@ -170,4 +170,6 @@ interface IConnectivityManager in PendingIntent operation); void releaseNetworkRequest(in NetworkRequest networkRequest); + + int getRestoreDefaultNetworkDelay(int networkType); } diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 1c18ba5..7e8b1f1 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -24,85 +24,39 @@ import android.os.Messenger; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; -import android.util.SparseArray; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; /** - * A Utility class for handling NetworkRequests. - * - * Created by bearer-specific code to handle tracking requests, scores, - * network data and handle communicating with ConnectivityService. Two - * abstract methods: connect and disconnect are used to act on the - * underlying bearer code. Connect is called when we have a NetworkRequest - * and our score is better than the current handling network's score, while - * disconnect is used when ConnectivityService requests a disconnect. + * A Utility class for handling for communicating between bearer-specific + * code and ConnectivityService. * * A bearer may have more than one NetworkAgent if it can simultaneously * support separate networks (IMS / Internet / MMS Apns on cellular, or - * perhaps connections with different SSID or P2P for Wi-Fi). The bearer - * code should pass its NetworkAgents the NetworkRequests each NetworkAgent - * can handle, demultiplexing for different network types. The bearer code - * can also filter out requests it can never handle. + * perhaps connections with different SSID or P2P for Wi-Fi). * - * Each NetworkAgent needs to be given a score and NetworkCapabilities for - * their potential network. While disconnected, the NetworkAgent will check - * each time its score changes or a NetworkRequest changes to see if - * the NetworkAgent can provide a higher scored network for a NetworkRequest - * that the NetworkAgent's NetworkCapabilties can satisfy. This condition will - * trigger a connect request via connect(). After connection, connection data - * should be given to the NetworkAgent by the bearer, including LinkProperties - * NetworkCapabilties and NetworkInfo. After that the NetworkAgent will register - * with ConnectivityService and forward the data on. * @hide */ public abstract class NetworkAgent extends Handler { - private final SparseArray<NetworkRequestAndScore> mNetworkRequests = new SparseArray<>(); - private boolean mConnectionRequested = false; - - private AsyncChannel mAsyncChannel; + private volatile AsyncChannel mAsyncChannel; private final String LOG_TAG; private static final boolean DBG = true; private static final boolean VDBG = true; - // TODO - this class shouldn't cache data or it runs the risk of getting out of sync - // Make the API require each of these when any is updated so we have the data we need, - // without caching. - private LinkProperties mLinkProperties; - private NetworkInfo mNetworkInfo; - private NetworkCapabilities mNetworkCapabilities; - private int mNetworkScore; - private boolean mRegistered = false; private final Context mContext; - private AtomicBoolean mHasRequests = new AtomicBoolean(false); - - // TODO - add a name member for logging purposes. - - protected final Object mLockObj = new Object(); - + private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>(); private static final int BASE = Protocol.BASE_NETWORK_AGENT; /** - * Sent by self to queue up a new/modified request. - * obj = NetworkRequestAndScore - */ - private static final int CMD_ADD_REQUEST = BASE + 1; - - /** - * Sent by self to queue up the removal of a request. - * obj = NetworkRequest - */ - private static final int CMD_REMOVE_REQUEST = BASE + 2; - - /** * Sent by ConnectivityService to the NetworkAgent to inform it of * suspected connectivity problems on its network. The NetworkAgent * should take steps to verify and correct connectivity. */ - public static final int CMD_SUSPECT_BAD = BASE + 3; + public static final int CMD_SUSPECT_BAD = BASE; /** * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to @@ -110,84 +64,63 @@ public abstract class NetworkAgent extends Handler { * Sent when the NetworkInfo changes, mainly due to change of state. * obj = NetworkInfo */ - public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 4; + public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1; /** * Sent by the NetworkAgent to ConnectivityService to pass the current * NetworkCapabilties. * obj = NetworkCapabilities */ - public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 5; + public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2; /** * Sent by the NetworkAgent to ConnectivityService to pass the current * NetworkProperties. * obj = NetworkProperties */ - public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 6; + public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3; /** * Sent by the NetworkAgent to ConnectivityService to pass the current * network score. - * arg1 = network score int + * obj = network score Integer */ - public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 7; + public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4; - public NetworkAgent(Looper looper, Context context, String logTag) { + public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, + NetworkCapabilities nc, LinkProperties lp, int score) { super(looper); LOG_TAG = logTag; mContext = context; - } - - /** - * When conditions are right, register with ConnectivityService. - * Connditions include having a well defined network and a request - * that justifies it. The NetworkAgent will remain registered until - * disconnected. - * TODO - this should have all data passed in rather than caching - */ - private void registerSelf() { - synchronized(mLockObj) { - if (!mRegistered && mConnectionRequested && - mNetworkInfo != null && mNetworkInfo.isConnected() && - mNetworkCapabilities != null && - mLinkProperties != null && - mNetworkScore != 0) { - if (DBG) log("Registering NetworkAgent"); - mRegistered = true; - ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(mNetworkInfo), - new LinkProperties(mLinkProperties), - new NetworkCapabilities(mNetworkCapabilities), mNetworkScore); - } else if (DBG && !mRegistered) { - String err = "Not registering due to "; - if (mConnectionRequested == false) err += "no Connect requested "; - if (mNetworkInfo == null) err += "null NetworkInfo "; - if (mNetworkInfo != null && mNetworkInfo.isConnected() == false) { - err += "NetworkInfo disconnected "; - } - if (mLinkProperties == null) err += "null LinkProperties "; - if (mNetworkCapabilities == null) err += "null NetworkCapabilities "; - if (mNetworkScore == 0) err += "null NetworkScore"; - log(err); - } + if (ni == null || nc == null || lp == null) { + throw new IllegalArgumentException(); } + + if (DBG) log("Registering NetworkAgent"); + ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni), + new LinkProperties(lp), new NetworkCapabilities(nc), score); } @Override public void handleMessage(Message msg) { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { - synchronized (mLockObj) { - if (mAsyncChannel != null) { - log("Received new connection while already connected!"); - } else { - if (DBG) log("NetworkAgent fully connected"); - mAsyncChannel = new AsyncChannel(); - mAsyncChannel.connected(null, this, msg.replyTo); - mAsyncChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, - AsyncChannel.STATUS_SUCCESSFUL); + if (mAsyncChannel != null) { + log("Received new connection while already connected!"); + } else { + if (DBG) log("NetworkAgent fully connected"); + AsyncChannel ac = new AsyncChannel(); + ac.connected(null, this, msg.replyTo); + ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, + AsyncChannel.STATUS_SUCCESSFUL); + synchronized (mPreConnectedQueue) { + mAsyncChannel = ac; + for (Message m : mPreConnectedQueue) { + ac.sendMessage(m); + } + mPreConnectedQueue.clear(); } } break; @@ -199,213 +132,69 @@ public abstract class NetworkAgent extends Handler { } case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { if (DBG) log("NetworkAgent channel lost"); - disconnect(); - clear(); + // let the client know CS is done with us. + unwanted(); + synchronized (mPreConnectedQueue) { + mAsyncChannel = null; + } break; } case CMD_SUSPECT_BAD: { log("Unhandled Message " + msg); break; } - case CMD_ADD_REQUEST: { - handleAddRequest(msg); - break; - } - case CMD_REMOVE_REQUEST: { - handleRemoveRequest(msg); - break; - } - } - } - - private void clear() { - synchronized(mLockObj) { - mNetworkRequests.clear(); - mHasRequests.set(false); - mConnectionRequested = false; - mAsyncChannel = null; - mRegistered = false; - } - } - - private static class NetworkRequestAndScore { - NetworkRequest req; - int score; - - NetworkRequestAndScore(NetworkRequest networkRequest, int score) { - req = networkRequest; - this.score = score; } } - private void handleAddRequest(Message msg) { - NetworkRequestAndScore n = (NetworkRequestAndScore)msg.obj; - // replaces old request, updating score - mNetworkRequests.put(n.req.requestId, n); - mHasRequests.set(true); - evalScores(); - } - - private void handleRemoveRequest(Message msg) { - NetworkRequest networkRequest = (NetworkRequest)msg.obj; - - if (mNetworkRequests.get(networkRequest.requestId) != null) { - mNetworkRequests.remove(networkRequest.requestId); - if (mNetworkRequests.size() == 0) mHasRequests.set(false); - evalScores(); - } - } - - /** - * Called to go through our list of requests and see if we're - * good enough to try connecting, or if we have gotten worse and - * need to disconnect. - * - * Once we are registered, does nothing: we disconnect when requested via - * CMD_CHANNEL_DISCONNECTED, generated by either a loss of connection - * between modules (bearer or ConnectivityService dies) or more commonly - * when the NetworkInfo reports to ConnectivityService it is disconnected. - */ - private void evalScores() { - synchronized(mLockObj) { - if (mRegistered) { - if (VDBG) log("evalScores - already connected - size=" + mNetworkRequests.size()); - // already trying - return; - } - if (VDBG) log("evalScores!"); - for (int i=0; i < mNetworkRequests.size(); i++) { - int score = mNetworkRequests.valueAt(i).score; - if (VDBG) log(" checking request Min " + score + " vs my score " + mNetworkScore); - if (score < mNetworkScore) { - // have a request that has a lower scored network servicing it - // (or no network) than we could provide, so let's connect! - mConnectionRequested = true; - connect(); - return; - } - } - // Our score is not high enough to satisfy any current request. - // This can happen if our score goes down after a connection is - // requested but before we actually connect. In this case, disconnect - // rather than continue trying - there's no point connecting if we know - // we'll just be torn down as soon as we do. - if (mConnectionRequested) { - mConnectionRequested = false; - disconnect(); + private void queueOrSendMessage(int what, Object obj) { + synchronized (mPreConnectedQueue) { + if (mAsyncChannel != null) { + mAsyncChannel.sendMessage(what, obj); + } else { + Message msg = Message.obtain(); + msg.what = what; + msg.obj = obj; + mPreConnectedQueue.add(msg); } } } - public void addNetworkRequest(NetworkRequest networkRequest, int score) { - if (DBG) log("adding NetworkRequest " + networkRequest + " with score " + score); - sendMessage(obtainMessage(CMD_ADD_REQUEST, - new NetworkRequestAndScore(networkRequest, score))); - } - - public void removeNetworkRequest(NetworkRequest networkRequest) { - if (DBG) log("removing NetworkRequest " + networkRequest); - sendMessage(obtainMessage(CMD_REMOVE_REQUEST, networkRequest)); - } - /** * Called by the bearer code when it has new LinkProperties data. - * If we're a registered NetworkAgent, this new data will get forwarded on, - * otherwise we store a copy in anticipation of registering. This call - * may also prompt registration if it causes the NetworkAgent to meet - * the conditions (fully configured, connected, satisfys a request and - * has sufficient score). */ public void sendLinkProperties(LinkProperties linkProperties) { - linkProperties = new LinkProperties(linkProperties); - synchronized(mLockObj) { - mLinkProperties = linkProperties; - if (mAsyncChannel != null) { - mAsyncChannel.sendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, linkProperties); - } else { - registerSelf(); - } - } + queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties)); } /** * Called by the bearer code when it has new NetworkInfo data. - * If we're a registered NetworkAgent, this new data will get forwarded on, - * otherwise we store a copy in anticipation of registering. This call - * may also prompt registration if it causes the NetworkAgent to meet - * the conditions (fully configured, connected, satisfys a request and - * has sufficient score). */ public void sendNetworkInfo(NetworkInfo networkInfo) { - networkInfo = new NetworkInfo(networkInfo); - synchronized(mLockObj) { - mNetworkInfo = networkInfo; - if (mAsyncChannel != null) { - mAsyncChannel.sendMessage(EVENT_NETWORK_INFO_CHANGED, networkInfo); - } else { - registerSelf(); - } - } + queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo)); } /** * Called by the bearer code when it has new NetworkCapabilities data. - * If we're a registered NetworkAgent, this new data will get forwarded on, - * otherwise we store a copy in anticipation of registering. This call - * may also prompt registration if it causes the NetworkAgent to meet - * the conditions (fully configured, connected, satisfys a request and - * has sufficient score). - * Note that if these capabilities make the network non-useful, - * ConnectivityServce will tear this network down. */ public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) { - networkCapabilities = new NetworkCapabilities(networkCapabilities); - synchronized(mLockObj) { - mNetworkCapabilities = networkCapabilities; - if (mAsyncChannel != null) { - mAsyncChannel.sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, networkCapabilities); - } else { - registerSelf(); - } - } - } - - public NetworkCapabilities getNetworkCapabilities() { - synchronized(mLockObj) { - return new NetworkCapabilities(mNetworkCapabilities); - } + queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, + new NetworkCapabilities(networkCapabilities)); } /** * Called by the bearer code when it has a new score for this network. - * If we're a registered NetworkAgent, this new data will get forwarded on, - * otherwise we store a copy. */ - public synchronized void sendNetworkScore(int score) { - synchronized(mLockObj) { - mNetworkScore = score; - evalScores(); - if (mAsyncChannel != null) { - mAsyncChannel.sendMessage(EVENT_NETWORK_SCORE_CHANGED, mNetworkScore); - } else { - registerSelf(); - } - } - } - - public boolean hasRequests() { - return mHasRequests.get(); + public void sendNetworkScore(int score) { + queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score)); } - public boolean isConnectionRequested() { - synchronized(mLockObj) { - return mConnectionRequested; - } - } - - - abstract protected void connect(); - abstract protected void disconnect(); + /** + * Called when ConnectivityService has indicated they no longer want this network. + * The parent factory should (previously) have received indication of the change + * as well, either canceling NetworkRequests or altering their score such that this + * network won't be immediately requested again. + */ + abstract protected void unwanted(); protected void log(String s) { Log.d(LOG_TAG, "NetworkAgent: " + s); diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java new file mode 100644 index 0000000..a20e8e7 --- /dev/null +++ b/core/java/android/net/NetworkFactory.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.util.AsyncChannel; +import com.android.internal.util.Protocol; + +/** + * A NetworkFactory is an entity that creates NetworkAgent objects. + * The bearers register with ConnectivityService using {@link #register} and + * their factory will start receiving scored NetworkRequests. NetworkRequests + * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by + * overridden function. All of these can be dynamic - changing NetworkCapabilities + * or score forces re-evaluation of all current requests. + * + * If any requests pass the filter some overrideable functions will be called. + * If the bearer only cares about very simple start/stopNetwork callbacks, those + * functions can be overridden. If the bearer needs more interaction, it can + * override addNetworkRequest and removeNetworkRequest which will give it each + * request that passes their current filters. + * @hide + **/ +public class NetworkFactory extends Handler { + private static final boolean DBG = true; + + private static final int BASE = Protocol.BASE_NETWORK_FACTORY; + /** + * Pass a network request to the bearer. If the bearer believes it can + * satisfy the request it should connect to the network and create a + * NetworkAgent. Once the NetworkAgent is fully functional it will + * register itself with ConnectivityService using registerNetworkAgent. + * If the bearer cannot immediately satisfy the request (no network, + * user disabled the radio, lower-scored network) it should remember + * any NetworkRequests it may be able to satisfy in the future. It may + * disregard any that it will never be able to service, for example + * those requiring a different bearer. + * msg.obj = NetworkRequest + * msg.arg1 = score - the score of the any network currently satisfying this + * request. If this bearer knows in advance it cannot + * exceed this score it should not try to connect, holding the request + * for the future. + * Note that subsequent events may give a different (lower + * or higher) score for this request, transmitted to each + * NetworkFactory through additional CMD_REQUEST_NETWORK msgs + * with the same NetworkRequest but an updated score. + * Also, network conditions may change for this bearer + * allowing for a better score in the future. + */ + public static final int CMD_REQUEST_NETWORK = BASE; + + /** + * Cancel a network request + * msg.obj = NetworkRequest + */ + public static final int CMD_CANCEL_REQUEST = BASE + 1; + + /** + * Internally used to set our best-guess score. + * msg.arg1 = new score + */ + private static final int CMD_SET_SCORE = BASE + 2; + + /** + * Internally used to set our current filter for coarse bandwidth changes with + * technology changes. + * msg.obj = new filter + */ + private static final int CMD_SET_FILTER = BASE + 3; + + private final Context mContext; + private final String LOG_TAG; + + private final SparseArray<NetworkRequestInfo> mNetworkRequests = + new SparseArray<NetworkRequestInfo>(); + + private int mScore; + private NetworkCapabilities mCapabilityFilter; + + private int mRefCount = 0; + private Messenger mMessenger = null; + + public NetworkFactory(Looper looper, Context context, String logTag, + NetworkCapabilities filter) { + super(looper); + LOG_TAG = logTag; + mContext = context; + mCapabilityFilter = filter; + } + + public void register() { + if (DBG) log("Registering NetworkFactory"); + if (mMessenger == null) { + mMessenger = new Messenger(this); + ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG); + } + } + + public void unregister() { + if (DBG) log("Unregistering NetworkFactory"); + if (mMessenger != null) { + ConnectivityManager.from(mContext).unregisterNetworkFactory(mMessenger); + mMessenger = null; + } + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CMD_REQUEST_NETWORK: { + handleAddRequest((NetworkRequest)msg.obj, msg.arg1); + break; + } + case CMD_CANCEL_REQUEST: { + handleRemoveRequest((NetworkRequest) msg.obj); + break; + } + case CMD_SET_SCORE: { + handleSetScore(msg.arg1); + break; + } + case CMD_SET_FILTER: { + handleSetFilter((NetworkCapabilities) msg.obj); + break; + } + } + } + + private class NetworkRequestInfo { + public final NetworkRequest request; + public int score; + public boolean requested; // do we have a request outstanding, limited by score + + public NetworkRequestInfo(NetworkRequest request, int score) { + this.request = request; + this.score = score; + this.requested = false; + } + } + + private void handleAddRequest(NetworkRequest request, int score) { + NetworkRequestInfo n = mNetworkRequests.get(request.requestId); + if (n == null) { + n = new NetworkRequestInfo(request, score); + mNetworkRequests.put(n.request.requestId, n); + } else { + n.score = score; + } + if (DBG) log("got request " + request + " with score " + score); + if (DBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); + + evalRequest(n); + } + + private void handleRemoveRequest(NetworkRequest request) { + NetworkRequestInfo n = mNetworkRequests.get(request.requestId); + if (n != null && n.requested) { + mNetworkRequests.remove(request.requestId); + releaseNetworkFor(n.request); + } + } + + private void handleSetScore(int score) { + mScore = score; + evalRequests(); + } + + private void handleSetFilter(NetworkCapabilities netCap) { + mCapabilityFilter = netCap; + evalRequests(); + } + + /** + * Overridable function to provide complex filtering. + * Called for every request every time a new NetworkRequest is seen + * and whenever the filterScore or filterNetworkCapabilities change. + * + * acceptRequest can be overriden to provide complex filter behavior + * for the incoming requests + * + * For output, this class will call {@link #needNetworkFor} and + * {@link #releaseNetworkFor} for every request that passes the filters. + * If you don't need to see every request, you can leave the base + * implementations of those two functions and instead override + * {@link #startNetwork} and {@link #stopNetwork}. + * + * If you want to see every score fluctuation on every request, set + * your score filter to a very high number and watch {@link #needNetworkFor}. + * + * @return {@code true} to accept the request. + */ + public boolean acceptRequest(NetworkRequest request, int score) { + return true; + } + + private void evalRequest(NetworkRequestInfo n) { + if (n.requested == false && n.score < mScore && + n.request.networkCapabilities.satisfiedByNetworkCapabilities( + mCapabilityFilter) && acceptRequest(n.request, n.score)) { + needNetworkFor(n.request, n.score); + n.requested = true; + } else if (n.requested == true && + (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities( + mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) { + releaseNetworkFor(n.request); + n.requested = false; + } + } + + private void evalRequests() { + for (int i = 0; i < mNetworkRequests.size(); i++) { + NetworkRequestInfo n = mNetworkRequests.valueAt(i); + + evalRequest(n); + } + } + + // override to do simple mode (request independent) + protected void startNetwork() { } + protected void stopNetwork() { } + + // override to do fancier stuff + protected void needNetworkFor(NetworkRequest networkRequest, int score) { + if (++mRefCount == 1) startNetwork(); + } + + protected void releaseNetworkFor(NetworkRequest networkRequest) { + if (--mRefCount == 0) stopNetwork(); + } + + + public void addNetworkRequest(NetworkRequest networkRequest, int score) { + sendMessage(obtainMessage(CMD_REQUEST_NETWORK, + new NetworkRequestInfo(networkRequest, score))); + } + + public void removeNetworkRequest(NetworkRequest networkRequest) { + sendMessage(obtainMessage(CMD_CANCEL_REQUEST, networkRequest)); + } + + public void setScoreFilter(int score) { + sendMessage(obtainMessage(CMD_SET_SCORE, score, 0)); + } + + public void setCapabilityFilter(NetworkCapabilities netCap) { + sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap))); + } + + protected void log(String s) { + Log.d(LOG_TAG, s); + } +} diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 9e656ee..d279412 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -188,6 +188,15 @@ public class NetworkInfo implements Parcelable { } /** + * @hide + */ + public void setType(int type) { + synchronized (this) { + mNetworkType = type; + } + } + + /** * Return a network-type-specific integer describing the subtype * of the network. * @return the network subtype @@ -198,7 +207,10 @@ public class NetworkInfo implements Parcelable { } } - void setSubtype(int subtype, String subtypeName) { + /** + * @hide + */ + public void setSubtype(int subtype, String subtypeName) { synchronized (this) { mSubtype = subtype; mSubtypeName = subtypeName; diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 480cb05..47377e9 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -47,19 +47,19 @@ public class NetworkRequest implements Parcelable { public final int requestId; /** - * Set for legacy requests and the default. + * Set for legacy requests and the default. Set to TYPE_NONE for none. * Causes CONNECTIVITY_ACTION broadcasts to be sent. * @hide */ - public final boolean needsBroadcasts; + public final int legacyType; /** * @hide */ - public NetworkRequest(NetworkCapabilities nc, boolean needsBroadcasts, int rId) { + public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId) { requestId = rId; networkCapabilities = nc; - this.needsBroadcasts = needsBroadcasts; + this.legacyType = legacyType; } /** @@ -68,7 +68,7 @@ public class NetworkRequest implements Parcelable { public NetworkRequest(NetworkRequest that) { networkCapabilities = new NetworkCapabilities(that.networkCapabilities); requestId = that.requestId; - needsBroadcasts = that.needsBroadcasts; + this.legacyType = that.legacyType; } // implement the Parcelable interface @@ -77,16 +77,16 @@ public class NetworkRequest implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(networkCapabilities, flags); - dest.writeInt(needsBroadcasts ? 1 : 0); + dest.writeInt(legacyType); dest.writeInt(requestId); } public static final Creator<NetworkRequest> CREATOR = new Creator<NetworkRequest>() { public NetworkRequest createFromParcel(Parcel in) { NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null); - boolean needsBroadcasts = (in.readInt() == 1); + int legacyType = in.readInt(); int requestId = in.readInt(); - NetworkRequest result = new NetworkRequest(nc, needsBroadcasts, requestId); + NetworkRequest result = new NetworkRequest(nc, legacyType, requestId); return result; } public NetworkRequest[] newArray(int size) { @@ -95,14 +95,14 @@ public class NetworkRequest implements Parcelable { }; public String toString() { - return "NetworkRequest [ id=" + requestId + ", needsBroadcasts=" + needsBroadcasts + + return "NetworkRequest [ id=" + requestId + ", legacyType=" + legacyType + ", " + networkCapabilities.toString() + " ]"; } public boolean equals(Object obj) { if (obj instanceof NetworkRequest == false) return false; NetworkRequest that = (NetworkRequest)obj; - return (that.needsBroadcasts == this.needsBroadcasts && + return (that.legacyType == this.legacyType && that.requestId == this.requestId && ((that.networkCapabilities == null && this.networkCapabilities == null) || (that.networkCapabilities != null && @@ -110,7 +110,7 @@ public class NetworkRequest implements Parcelable { } public int hashCode() { - return requestId + (needsBroadcasts ? 1013 : 2026) + + return requestId + (legacyType * 1013) + (networkCapabilities.hashCode() * 1051); } } diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java new file mode 100644 index 0000000..7f8bc9f --- /dev/null +++ b/core/java/android/os/FileBridge.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.SOCK_STREAM; + +import android.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import libcore.io.IoBridge; +import libcore.io.IoUtils; +import libcore.io.Memory; +import libcore.io.Streams; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.OutputStream; +import java.io.SyncFailedException; +import java.nio.ByteOrder; +import java.util.Arrays; + +/** + * Simple bridge that allows file access across process boundaries without + * returning the underlying {@link FileDescriptor}. This is useful when the + * server side needs to strongly assert that a client side is completely + * hands-off. + * + * @hide + */ +public class FileBridge extends Thread { + private static final String TAG = "FileBridge"; + + // TODO: consider extending to support bidirectional IO + + private static final int MSG_LENGTH = 8; + + /** CMD_WRITE [len] [data] */ + private static final int CMD_WRITE = 1; + /** CMD_FSYNC */ + private static final int CMD_FSYNC = 2; + + private FileDescriptor mTarget; + + private final FileDescriptor mServer = new FileDescriptor(); + private final FileDescriptor mClient = new FileDescriptor(); + + private volatile boolean mClosed; + + public FileBridge() { + try { + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient); + } catch (ErrnoException e) { + throw new RuntimeException("Failed to create bridge"); + } + } + + public boolean isClosed() { + return mClosed; + } + + public void setTargetFile(FileDescriptor target) { + mTarget = target; + } + + public FileDescriptor getClientSocket() { + return mClient; + } + + @Override + public void run() { + final byte[] temp = new byte[8192]; + try { + while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) { + final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN); + + if (cmd == CMD_WRITE) { + // Shuttle data into local file + int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN); + while (len > 0) { + int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len)); + IoBridge.write(mTarget, temp, 0, n); + len -= n; + } + + } else if (cmd == CMD_FSYNC) { + // Sync and echo back to confirm + Os.fsync(mTarget); + IoBridge.write(mServer, temp, 0, MSG_LENGTH); + } + } + + // Client was closed; one last fsync + Os.fsync(mTarget); + + } catch (ErrnoException e) { + Log.e(TAG, "Failed during bridge: ", e); + } catch (IOException e) { + Log.e(TAG, "Failed during bridge: ", e); + } finally { + IoUtils.closeQuietly(mTarget); + IoUtils.closeQuietly(mServer); + IoUtils.closeQuietly(mClient); + mClosed = true; + } + } + + public static class FileBridgeOutputStream extends OutputStream { + private final FileDescriptor mClient; + private final byte[] mTemp = new byte[MSG_LENGTH]; + + public FileBridgeOutputStream(FileDescriptor client) { + mClient = client; + } + + @Override + public void close() throws IOException { + IoBridge.closeAndSignalBlockedThreads(mClient); + } + + @Override + public void flush() throws IOException { + Memory.pokeInt(mTemp, 0, CMD_FSYNC, ByteOrder.BIG_ENDIAN); + IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); + + // Wait for server to ack + if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) { + if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == CMD_FSYNC) { + return; + } + } + + throw new SyncFailedException("Failed to fsync() across bridge"); + } + + @Override + public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException { + Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount); + Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN); + Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN); + IoBridge.write(mClient, mTemp, 0, MSG_LENGTH); + IoBridge.write(mClient, buffer, byteOffset, byteCount); + } + + @Override + public void write(int oneByte) throws IOException { + Streams.writeSingleByte(this, oneByte); + } + } +} diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 57ed979..474192f 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -70,6 +70,8 @@ public final class Trace { public static final long TRACE_TAG_DALVIK = 1L << 14; /** @hide */ public static final long TRACE_TAG_RS = 1L << 15; + /** @hide */ + public static final long TRACE_TAG_BIONIC = 1L << 16; private static final long TRACE_TAG_NOT_READY = 1L << 63; private static final int MAX_SECTION_NAME_LEN = 127; diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index cb0f142..c1d4d4c 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -74,7 +74,6 @@ public abstract class Vibrator { * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type. * For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or * {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls. - * @hide */ public void vibrate(long milliseconds, int streamHint) { vibrate(Process.myUid(), mPackageName, milliseconds, streamHint); @@ -126,7 +125,6 @@ public abstract class Vibrator { * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type. * For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or * {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls. - * @hide */ public void vibrate(long[] pattern, int repeat, int streamHint) { vibrate(Process.myUid(), mPackageName, pattern, repeat, streamHint); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b53ea81..6db78f4 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1152,8 +1152,6 @@ public final class ContactsContract { * address book index, which is usually the first letter of the sort key. * When this parameter is supplied, the row counts are returned in the * cursor extras bundle. - * - * @hide */ public final static class ContactCounts { @@ -1163,7 +1161,24 @@ public final class ContactsContract { * first letter of the sort key. This parameter does not affect the main * content of the cursor. * - * @hide + * <p> + * <pre> + * Example: + * Uri uri = Contacts.CONTENT_URI.buildUpon() + * .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true") + * .build(); + * Cursor cursor = getContentResolver().query(uri, + * new String[] {Contacts.DISPLAY_NAME}, + * null, null, null); + * Bundle bundle = cursor.getExtras(); + * if (bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES) && + * bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS)) { + * String sections[] = + * bundle.getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES); + * int counts[] = bundle.getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); + * } + * </pre> + * </p> */ public static final String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras"; @@ -1171,8 +1186,6 @@ public final class ContactsContract { * The array of address book index titles, which are returned in the * same order as the data in the cursor. * <p>TYPE: String[]</p> - * - * @hide */ public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles"; @@ -1180,8 +1193,6 @@ public final class ContactsContract { * The array of group counts for the corresponding group. Contains the same number * of elements as the EXTRA_ADDRESS_BOOK_INDEX_TITLES array. * <p>TYPE: int[]</p> - * - * @hide */ public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts"; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e9ffc52..bec401e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4461,6 +4461,12 @@ public final class Settings { INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF; /** + * Whether the device should wake when the wake gesture sensor detects motion. + * @hide + */ + public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled"; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. diff --git a/core/java/android/speech/tts/Markup.java b/core/java/android/speech/tts/Markup.java new file mode 100644 index 0000000..c886e5d --- /dev/null +++ b/core/java/android/speech/tts/Markup.java @@ -0,0 +1,537 @@ +package android.speech.tts; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class that provides markup to a synthesis request to control aspects of speech. + * <p> + * Markup itself is a feature agnostic data format; the {@link Utterance} class defines the currently + * available set of features and should be used to construct instances of the Markup class. + * </p> + * <p> + * A marked up sentence is a tree. Each node has a type, an optional plain text, a set of + * parameters, and a list of children. + * The <b>type</b> defines what it contains, e.g. "text", "date", "measure", etc. A Markup node + * can be either a part of sentence (often a leaf node), or node altering some property of its + * children (node with children). The top level node has to be of type "utterance" and its children + * are synthesized in order. + * The <b>plain text</b> is optional except for the top level node. If the synthesis engine does not + * support Markup at all, it should use the plain text of the top level node. If an engine does not + * recognize or support a node type, it will try to use the plain text of that node if provided. If + * the plain text is null, it will synthesize its children in order. + * <b>Parameters</b> are key-value pairs specific to each node type. In case of a date node the + * parameters may be for example "month: 7" and "day: 10". + * The <b>nested markups</b> are children and can for example be used to nest semiotic classes (a + * measure may have a node of type "decimal" as its child) or to modify some property of its + * children. See "plain text" on how they are processed if the parent of the children is unknown to + * the engine. + * <p> + */ +public final class Markup implements Parcelable { + + private String mType; + private String mPlainText; + + private Bundle mParameters = new Bundle(); + private List<Markup> mNestedMarkups = new ArrayList<Markup>(); + + private static final String TYPE = "type"; + private static final String PLAIN_TEXT = "plain_text"; + private static final String MARKUP = "markup"; + + private static final String IDENTIFIER_REGEX = "([0-9a-z_]+)"; + private static final Pattern legalIdentifierPattern = Pattern.compile(IDENTIFIER_REGEX); + + /** + * Constructs an empty markup. + */ + public Markup() {} + + /** + * Constructs a markup of the given type. + */ + public Markup(String type) { + setType(type); + } + + /** + * Returns the type of this node; can be null. + */ + public String getType() { + return mType; + } + + /** + * Sets the type of this node. can be null. May only contain [0-9a-z_]. + */ + public void setType(String type) { + if (type != null) { + Matcher matcher = legalIdentifierPattern.matcher(type); + if (!matcher.matches()) { + throw new IllegalArgumentException("Type cannot be empty and may only contain " + + "0-9, a-z and underscores."); + } + } + mType = type; + } + + /** + * Returns this node's plain text; can be null. + */ + public String getPlainText() { + return mPlainText; + } + + /** + * Sets this nodes's plain text; can be null. + */ + public void setPlainText(String plainText) { + mPlainText = plainText; + } + + /** + * Adds or modifies a parameter. + * @param key The key; may only contain [0-9a-z_] and cannot be "type" or "plain_text". + * @param value The value. + * @throws An {@link IllegalArgumentException} if the key is null or empty. + * @return this + */ + public Markup setParameter(String key, String value) { + if (key == null || key.isEmpty()) { + throw new IllegalArgumentException("Key cannot be null or empty."); + } + if (key.equals("type")) { + throw new IllegalArgumentException("Key cannot be \"type\"."); + } + if (key.equals("plain_text")) { + throw new IllegalArgumentException("Key cannot be \"plain_text\"."); + } + Matcher matcher = legalIdentifierPattern.matcher(key); + if (!matcher.matches()) { + throw new IllegalArgumentException("Key may only contain 0-9, a-z and underscores."); + } + + if (value != null) { + mParameters.putString(key, value); + } else { + removeParameter(key); + } + return this; + } + + /** + * Removes the parameter with the given key + */ + public void removeParameter(String key) { + mParameters.remove(key); + } + + /** + * Returns the value of the parameter. + * @param key The parameter key. + * @return The value of the parameter or null if the parameter is not set. + */ + public String getParameter(String key) { + return mParameters.getString(key); + } + + /** + * Returns the number of parameters that have been set. + */ + public int parametersSize() { + return mParameters.size(); + } + + /** + * Appends a child to the list of children + * @param markup The child. + * @return This instance. + * @throws {@link IllegalArgumentException} if markup is null. + */ + public Markup addNestedMarkup(Markup markup) { + if (markup == null) { + throw new IllegalArgumentException("Nested markup cannot be null"); + } + mNestedMarkups.add(markup); + return this; + } + + /** + * Removes the given node from its children. + * @param markup The child to remove. + * @return True if this instance was modified by this operation, false otherwise. + */ + public boolean removeNestedMarkup(Markup markup) { + return mNestedMarkups.remove(markup); + } + + /** + * Returns the index'th child. + * @param i The index of the child. + * @return The child. + * @throws {@link IndexOutOfBoundsException} if i < 0 or i >= nestedMarkupSize() + */ + public Markup getNestedMarkup(int i) { + return mNestedMarkups.get(i); + } + + + /** + * Returns the number of children. + */ + public int nestedMarkupSize() { + return mNestedMarkups.size(); + } + + /** + * Returns a string representation of this Markup instance. Can be deserialized back to a Markup + * instance with markupFromString(). + */ + public String toString() { + StringBuilder out = new StringBuilder(); + if (mType != null) { + out.append(TYPE + ": \"" + mType + "\""); + } + if (mPlainText != null) { + out.append(out.length() > 0 ? " " : ""); + out.append(PLAIN_TEXT + ": \"" + escapeQuotedString(mPlainText) + "\""); + } + // Sort the parameters alphabetically by key so we have a stable output. + SortedMap<String, String> sortedMap = new TreeMap<String, String>(); + for (String key : mParameters.keySet()) { + sortedMap.put(key, mParameters.getString(key)); + } + for (Map.Entry<String, String> entry : sortedMap.entrySet()) { + out.append(out.length() > 0 ? " " : ""); + out.append(entry.getKey() + ": \"" + escapeQuotedString(entry.getValue()) + "\""); + } + for (Markup m : mNestedMarkups) { + out.append(out.length() > 0 ? " " : ""); + String nestedStr = m.toString(); + if (nestedStr.isEmpty()) { + out.append(MARKUP + " {}"); + } else { + out.append(MARKUP + " { " + m.toString() + " }"); + } + } + return out.toString(); + } + + /** + * Escapes backslashes and double quotes in the plain text and parameter values before this + * instance is written to a string. + * @param str The string to escape. + * @return The escaped string. + */ + private static String escapeQuotedString(String str) { + StringBuilder out = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '"') { + out.append("\\\""); + } else if (str.charAt(i) == '\\') { + out.append("\\\\"); + } else { + out.append(c); + } + } + return out.toString(); + } + + /** + * The reverse of the escape method, returning plain text and parameter values to their original + * form. + * @param str An escaped string. + * @return The unescaped string. + */ + private static String unescapeQuotedString(String str) { + StringBuilder out = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '\\') { + i++; + if (i >= str.length()) { + throw new IllegalArgumentException("Unterminated escape sequence in string: " + + str); + } + c = str.charAt(i); + if (c == '\\') { + out.append("\\"); + } else if (c == '"') { + out.append("\""); + } else { + throw new IllegalArgumentException("Unsupported escape sequence: \\" + c + + " in string " + str); + } + } else { + out.append(c); + } + } + return out.toString(); + } + + /** + * Returns true if the given string consists only of whitespace. + * @param str The string to check. + * @return True if the given string consists only of whitespace. + */ + private static boolean isWhitespace(String str) { + return Pattern.matches("\\s*", str); + } + + /** + * Parses the given string, and overrides the values of this instance with those contained + * in the given string. + * @param str The string to parse; can have superfluous whitespace. + * @return An empty string on success, else the remainder of the string that could not be + * parsed. + */ + private String fromReadableString(String str) { + while (!isWhitespace(str)) { + String newStr = matchValue(str); + if (newStr == null) { + newStr = matchMarkup(str); + + if (newStr == null) { + return str; + } + } + str = newStr; + } + return ""; + } + + // Matches: key : "value" + // where key is an identifier and value can contain escaped quotes + // there may be superflouous whitespace + // The value string may contain quotes and backslashes. + private static final String OPTIONAL_WHITESPACE = "\\s*"; + private static final String VALUE_REGEX = "((\\\\.|[^\\\"])*)"; + private static final String KEY_VALUE_REGEX = + "\\A" + OPTIONAL_WHITESPACE + // start of string + IDENTIFIER_REGEX + OPTIONAL_WHITESPACE + ":" + OPTIONAL_WHITESPACE + // key: + "\"" + VALUE_REGEX + "\""; // "value" + private static final Pattern KEY_VALUE_PATTERN = Pattern.compile(KEY_VALUE_REGEX); + + /** + * Tries to match a key-value pair at the start of the string. If found, add that as a parameter + * of this instance. + * @param str The string to parse. + * @return The remainder of the string without the parsed key-value pair on success, else null. + */ + private String matchValue(String str) { + // Matches: key: "value" + Matcher matcher = KEY_VALUE_PATTERN.matcher(str); + if (!matcher.find()) { + return null; + } + String key = matcher.group(1); + String value = matcher.group(2); + + if (key == null || value == null) { + return null; + } + String unescapedValue = unescapeQuotedString(value); + if (key.equals(TYPE)) { + this.mType = unescapedValue; + } else if (key.equals(PLAIN_TEXT)) { + this.mPlainText = unescapedValue; + } else { + setParameter(key, unescapedValue); + } + + return str.substring(matcher.group(0).length()); + } + + // matches 'markup {' + private static final Pattern OPEN_MARKUP_PATTERN = + Pattern.compile("\\A" + OPTIONAL_WHITESPACE + MARKUP + OPTIONAL_WHITESPACE + "\\{"); + // matches '}' + private static final Pattern CLOSE_MARKUP_PATTERN = + Pattern.compile("\\A" + OPTIONAL_WHITESPACE + "\\}"); + + /** + * Tries to parse a Markup specification from the start of the string. If so, add that markup to + * the list of nested Markup's of this instance. + * @param str The string to parse. + * @return The remainder of the string without the parsed Markup on success, else null. + */ + private String matchMarkup(String str) { + // find and strip "markup {" + Matcher matcher = OPEN_MARKUP_PATTERN.matcher(str); + + if (!matcher.find()) { + return null; + } + String strRemainder = str.substring(matcher.group(0).length()); + // parse and strip markup contents + Markup nestedMarkup = new Markup(); + strRemainder = nestedMarkup.fromReadableString(strRemainder); + + // find and strip "}" + Matcher matcherClose = CLOSE_MARKUP_PATTERN.matcher(strRemainder); + if (!matcherClose.find()) { + return null; + } + strRemainder = strRemainder.substring(matcherClose.group(0).length()); + + // Everything parsed, add markup + this.addNestedMarkup(nestedMarkup); + + // Return remainder + return strRemainder; + } + + /** + * Returns a Markup instance from the string representation generated by toString(). + * @param string The string representation generated by toString(). + * @return The new Markup instance. + * @throws {@link IllegalArgumentException} if the input cannot be correctly parsed. + */ + public static Markup markupFromString(String string) throws IllegalArgumentException { + Markup m = new Markup(); + if (m.fromReadableString(string).isEmpty()) { + return m; + } else { + throw new IllegalArgumentException("Cannot parse input to Markup"); + } + } + + /** + * Compares the specified object with this Markup for equality. + * @return True if the given object is a Markup instance with the same type, plain text, + * parameters and the nested markups are also equal to each other and in the same order. + */ + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !(o instanceof Markup) ) return false; + Markup m = (Markup) o; + + if (nestedMarkupSize() != this.nestedMarkupSize()) { + return false; + } + + if (!(mType == null ? m.mType == null : mType.equals(m.mType))) { + return false; + } + if (!(mPlainText == null ? m.mPlainText == null : mPlainText.equals(m.mPlainText))) { + return false; + } + if (!equalBundles(mParameters, m.mParameters)) { + return false; + } + + for (int i = 0; i < this.nestedMarkupSize(); i++) { + if (!mNestedMarkups.get(i).equals(m.mNestedMarkups.get(i))) { + return false; + } + } + + return true; + } + + /** + * Checks if two bundles are equal to each other. Used by equals(o). + */ + private boolean equalBundles(Bundle one, Bundle two) { + if (one == null || two == null) { + return false; + } + + if(one.size() != two.size()) { + return false; + } + + Set<String> valuesOne = one.keySet(); + for(String key : valuesOne) { + Object valueOne = one.get(key); + Object valueTwo = two.get(key); + if (valueOne instanceof Bundle && valueTwo instanceof Bundle && + !equalBundles((Bundle) valueOne, (Bundle) valueTwo)) { + return false; + } else if (valueOne == null) { + if (valueTwo != null || !two.containsKey(key)) { + return false; + } + } else if(!valueOne.equals(valueTwo)) { + return false; + } + } + return true; + } + + /** + * Returns an unmodifiable list of the children. + * @return An unmodifiable list of children that throws an {@link UnsupportedOperationException} + * if an attempt is made to modify it + */ + public List<Markup> getNestedMarkups() { + return Collections.unmodifiableList(mNestedMarkups); + } + + /** + * @hide + */ + public Markup(Parcel in) { + mType = in.readString(); + mPlainText = in.readString(); + mParameters = in.readBundle(); + in.readList(mNestedMarkups, Markup.class.getClassLoader()); + } + + /** + * Creates a deep copy of the given markup. + */ + public Markup(Markup markup) { + mType = markup.mType; + mPlainText = markup.mPlainText; + mParameters = markup.mParameters; + for (Markup nested : markup.getNestedMarkups()) { + addNestedMarkup(new Markup(nested)); + } + } + + /** + * @hide + */ + public int describeContents() { + return 0; + } + + /** + * @hide + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mType); + dest.writeString(mPlainText); + dest.writeBundle(mParameters); + dest.writeList(mNestedMarkups); + } + + /** + * @hide + */ + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public Markup createFromParcel(Parcel in) { + return new Markup(in); + } + + public Markup[] newArray(int size) { + return new Markup[size]; + } + }; +} + diff --git a/core/java/android/speech/tts/SynthesisRequestV2.java b/core/java/android/speech/tts/SynthesisRequestV2.java index a1da49c..130e3f9 100644 --- a/core/java/android/speech/tts/SynthesisRequestV2.java +++ b/core/java/android/speech/tts/SynthesisRequestV2.java @@ -4,11 +4,12 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.speech.tts.TextToSpeechClient.UtteranceId; +import android.util.Log; /** * Service-side representation of a synthesis request from a V2 API client. Contains: * <ul> - * <li>The utterance to synthesize</li> + * <li>The markup object to synthesize containing the utterance.</li> * <li>The id of the utterance (String, result of {@link UtteranceId#toUniqueString()}</li> * <li>The synthesis voice name (String, result of {@link VoiceInfo#getName()})</li> * <li>Voice parameters (Bundle of parameters)</li> @@ -16,8 +17,11 @@ import android.speech.tts.TextToSpeechClient.UtteranceId; * </ul> */ public final class SynthesisRequestV2 implements Parcelable { - /** Synthesis utterance. */ - private final String mText; + + private static final String TAG = "SynthesisRequestV2"; + + /** Synthesis markup */ + private final Markup mMarkup; /** Synthesis id. */ private final String mUtteranceId; @@ -34,9 +38,9 @@ public final class SynthesisRequestV2 implements Parcelable { /** * Constructor for test purposes. */ - public SynthesisRequestV2(String text, String utteranceId, String voiceName, + public SynthesisRequestV2(Markup markup, String utteranceId, String voiceName, Bundle voiceParams, Bundle audioParams) { - this.mText = text; + this.mMarkup = markup; this.mUtteranceId = utteranceId; this.mVoiceName = voiceName; this.mVoiceParams = voiceParams; @@ -49,15 +53,18 @@ public final class SynthesisRequestV2 implements Parcelable { * @hide */ public SynthesisRequestV2(Parcel in) { - this.mText = in.readString(); + this.mMarkup = (Markup) in.readValue(Markup.class.getClassLoader()); this.mUtteranceId = in.readString(); this.mVoiceName = in.readString(); this.mVoiceParams = in.readBundle(); this.mAudioParams = in.readBundle(); } - SynthesisRequestV2(String text, String utteranceId, RequestConfig rconfig) { - this.mText = text; + /** + * Constructor to request the synthesis of a sentence. + */ + SynthesisRequestV2(Markup markup, String utteranceId, RequestConfig rconfig) { + this.mMarkup = markup; this.mUtteranceId = utteranceId; this.mVoiceName = rconfig.getVoice().getName(); this.mVoiceParams = rconfig.getVoiceParams(); @@ -71,7 +78,7 @@ public final class SynthesisRequestV2 implements Parcelable { */ @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mText); + dest.writeValue(mMarkup); dest.writeString(mUtteranceId); dest.writeString(mVoiceName); dest.writeBundle(mVoiceParams); @@ -82,7 +89,18 @@ public final class SynthesisRequestV2 implements Parcelable { * @return the text which should be synthesized. */ public String getText() { - return mText; + if (mMarkup.getPlainText() == null) { + Log.e(TAG, "Plaintext of markup is null."); + return ""; + } + return mMarkup.getPlainText(); + } + + /** + * @return the markup which should be synthesized. + */ + public Markup getMarkup() { + return mMarkup; } /** diff --git a/core/java/android/speech/tts/TextToSpeechClient.java b/core/java/android/speech/tts/TextToSpeechClient.java index 85f702b..e17b498 100644 --- a/core/java/android/speech/tts/TextToSpeechClient.java +++ b/core/java/android/speech/tts/TextToSpeechClient.java @@ -512,7 +512,6 @@ public class TextToSpeechClient { } } - /** * Connects the client to TTS service. This method returns immediately, and connects to the * service in the background. @@ -876,7 +875,7 @@ public class TextToSpeechClient { private static final String ACTION_QUEUE_SPEAK_NAME = "queueSpeak"; /** - * Speaks the string using the specified queuing strategy using current + * Speaks the string using the specified queuing strategy and the current * voice. This method is asynchronous, i.e. the method just adds the request * to the queue of TTS requests and then returns. The synthesis might not * have finished (or even started!) at the time when this method returns. @@ -887,12 +886,35 @@ public class TextToSpeechClient { * in {@link RequestCallbacks}. * @param config Synthesis request configuration. Can't be null. Has to contain a * voice. - * @param callbacks Synthesis request callbacks. If null, default request + * @param callbacks Synthesis request callbacks. If null, the default request * callbacks object will be used. */ public void queueSpeak(final String utterance, final UtteranceId utteranceId, final RequestConfig config, final RequestCallbacks callbacks) { + queueSpeak(createMarkupFromString(utterance), utteranceId, config, callbacks); + } + + /** + * Speaks the {@link Markup} (which can be constructed with {@link Utterance}) using + * the specified queuing strategy and the current voice. This method is + * asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even + * started!) at the time when this method returns. + * + * @param markup The Markup to be spoken. The written equivalent of the spoken + * text should be no longer than 1000 characters. + * @param utteranceId Unique identificator used to track the synthesis progress + * in {@link RequestCallbacks}. + * @param config Synthesis request configuration. Can't be null. Has to contain a + * voice. + * @param callbacks Synthesis request callbacks. If null, the default request + * callbacks object will be used. + */ + public void queueSpeak(final Markup markup, + final UtteranceId utteranceId, + final RequestConfig config, + final RequestCallbacks callbacks) { runAction(new Action(ACTION_QUEUE_SPEAK_NAME) { @Override public void run(ITextToSpeechService service) throws RemoteException { @@ -908,7 +930,7 @@ public class TextToSpeechClient { int queueResult = service.speakV2( getCallerIdentity(), - new SynthesisRequestV2(utterance, utteranceId.toUniqueString(), config)); + new SynthesisRequestV2(markup, utteranceId.toUniqueString(), config)); if (queueResult != Status.SUCCESS) { removeCallbackAndErr(utteranceId.toUniqueString(), queueResult); } @@ -931,12 +953,37 @@ public class TextToSpeechClient { * @param outputFile File to write the generated audio data to. * @param config Synthesis request configuration. Can't be null. Have to contain a * voice. - * @param callbacks Synthesis request callbacks. If null, default request + * @param callbacks Synthesis request callbacks. If null, the default request * callbacks object will be used. */ public void queueSynthesizeToFile(final String utterance, final UtteranceId utteranceId, final File outputFile, final RequestConfig config, final RequestCallbacks callbacks) { + queueSynthesizeToFile(createMarkupFromString(utterance), utteranceId, outputFile, config, callbacks); + } + + /** + * Synthesizes the given {@link Markup} (can be constructed with {@link Utterance}) + * to a file using the specified parameters. This method is asynchronous, i.e. the + * method just adds the request to the queue of TTS requests and then returns. The + * synthesis might not have finished (or even started!) at the time when this method + * returns. + * + * @param markup The Markup that should be synthesized. The written equivalent of + * the spoken text should be no longer than 1000 characters. + * @param utteranceId Unique identificator used to track the synthesis progress + * in {@link RequestCallbacks}. + * @param outputFile File to write the generated audio data to. + * @param config Synthesis request configuration. Can't be null. Have to contain a + * voice. + * @param callbacks Synthesis request callbacks. If null, the default request + * callbacks object will be used. + */ + public void queueSynthesizeToFile( + final Markup markup, + final UtteranceId utteranceId, + final File outputFile, final RequestConfig config, + final RequestCallbacks callbacks) { runAction(new Action(ACTION_QUEUE_SYNTHESIZE_TO_FILE) { @Override public void run(ITextToSpeechService service) throws RemoteException { @@ -964,8 +1011,7 @@ public class TextToSpeechClient { int queueResult = service.synthesizeToFileDescriptorV2(getCallerIdentity(), fileDescriptor, - new SynthesisRequestV2(utterance, utteranceId.toUniqueString(), - config)); + new SynthesisRequestV2(markup, utteranceId.toUniqueString(), config)); fileDescriptor.close(); if (queueResult != Status.SUCCESS) { removeCallbackAndErr(utteranceId.toUniqueString(), queueResult); @@ -981,6 +1027,13 @@ public class TextToSpeechClient { }); } + private static Markup createMarkupFromString(String str) { + return new Utterance() + .append(new Utterance.TtsText(str)) + .setNoWarningOnFallback(true) + .createMarkup(); + } + private static final String ACTION_QUEUE_SILENCE_NAME = "queueSilence"; /** diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 6b899d9..14a4024 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -352,6 +352,12 @@ public abstract class TextToSpeechService extends Service { params.putString(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS, "true"); } + String noWarning = request.getMarkup().getParameter(Utterance.KEY_NO_WARNING_ON_FALLBACK); + if (noWarning == null || noWarning.equals("false")) { + Log.w("TextToSpeechService", "The synthesis engine does not support Markup, falling " + + "back to the given plain text."); + } + // Build V1 request SynthesisRequest requestV1 = new SynthesisRequest(request.getText(), params); Locale locale = selectedVoice.getLocale(); @@ -856,14 +862,53 @@ public abstract class TextToSpeechService extends Service { } } + /** + * Estimate of the character count equivalent of a Markup instance. Calculated + * by summing the characters of all Markups of type "text". Each other node + * is counted as a single character, as the character count of other nodes + * is non-trivial to calculate and we don't want to accept arbitrarily large + * requests. + */ + private int estimateSynthesisLengthFromMarkup(Markup m) { + int size = 0; + if (m.getType() != null && + m.getType().equals("text") && + m.getParameter("text") != null) { + size += m.getParameter("text").length(); + } else if (m.getType() == null || + !m.getType().equals("utterance")) { + size += 1; + } + for (Markup nested : m.getNestedMarkups()) { + size += estimateSynthesisLengthFromMarkup(nested); + } + return size; + } + @Override public boolean isValid() { - if (mSynthesisRequest.getText() == null) { - Log.e(TAG, "null synthesis text"); + if (mSynthesisRequest.getMarkup() == null) { + Log.e(TAG, "No markup in request."); return false; } - if (mSynthesisRequest.getText().length() >= TextToSpeech.getMaxSpeechInputLength()) { - Log.w(TAG, "Text too long: " + mSynthesisRequest.getText().length() + " chars"); + String type = mSynthesisRequest.getMarkup().getType(); + if (type == null) { + Log.w(TAG, "Top level markup node should have type \"utterance\", not null"); + return false; + } else if (!type.equals("utterance")) { + Log.w(TAG, "Top level markup node should have type \"utterance\" instead of " + + "\"" + type + "\""); + return false; + } + + int estimate = estimateSynthesisLengthFromMarkup(mSynthesisRequest.getMarkup()); + if (estimate >= TextToSpeech.getMaxSpeechInputLength()) { + Log.w(TAG, "Text too long: estimated size of text was " + estimate + " chars."); + return false; + } + + if (estimate <= 0) { + Log.e(TAG, "null synthesis text"); return false; } diff --git a/core/java/android/speech/tts/Utterance.java b/core/java/android/speech/tts/Utterance.java new file mode 100644 index 0000000..0a29283 --- /dev/null +++ b/core/java/android/speech/tts/Utterance.java @@ -0,0 +1,595 @@ +package android.speech.tts; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class acts as a builder for {@link Markup} instances. + * <p> + * Each Utterance consists of a list of the semiotic classes ({@link Utterance.TtsCardinal} and + * {@link Utterance.TtsText}). + * <p>Each semiotic class can be supplied with morphosyntactic features + * (gender, animacy, multiplicity and case), it is up to the synthesis engine to use this + * information during synthesis. + * Examples where morphosyntactic features matter: + * <ul> + * <li>In French, the number one is verbalized differently based on the gender of the noun + * it modifies. "un homme" (one man) versus "une femme" (one woman). + * <li>In German the grammatical case (accusative, locative, etc) needs to be included to be + * verbalize correctly. In German you'd have the sentence "Sie haben 1 kilometer vor Ihnen" (You + * have 1 kilometer ahead of you), "1" in this case needs to become inflected to the accusative + * form ("einen") instead of the nominative form "ein". + * </p> + * <p> + * Utterance usage example: + * Markup m1 = new Utterance().append("The Eiffel Tower is") + * .append(new TtsCardinal(324)) + * .append("meters tall."); + * Markup m2 = new Utterance().append("Sie haben") + * .append(new TtsCardinal(1).setGender(Utterance.GENDER_MALE) + * .append("Tag frei."); + * </p> + */ +public class Utterance { + + /*** + * Toplevel type of markup representation. + */ + public static final String TYPE_UTTERANCE = "utterance"; + /*** + * The no_warning_on_fallback parameter can be set to "false" or "true", true indicating that + * no warning will be given when the synthesizer does not support Markup. This is used when + * the user only provides a string to the API instead of a markup. + */ + public static final String KEY_NO_WARNING_ON_FALLBACK = "no_warning_on_fallback"; + + // Gender. + public final static int GENDER_UNKNOWN = 0; + public final static int GENDER_NEUTRAL = 1; + public final static int GENDER_MALE = 2; + public final static int GENDER_FEMALE = 3; + + // Animacy. + public final static int ANIMACY_UNKNOWN = 0; + public final static int ANIMACY_ANIMATE = 1; + public final static int ANIMACY_INANIMATE = 2; + + // Multiplicity. + public final static int MULTIPLICITY_UNKNOWN = 0; + public final static int MULTIPLICITY_SINGLE = 1; + public final static int MULTIPLICITY_DUAL = 2; + public final static int MULTIPLICITY_PLURAL = 3; + + // Case. + public final static int CASE_UNKNOWN = 0; + public final static int CASE_NOMINATIVE = 1; + public final static int CASE_ACCUSATIVE = 2; + public final static int CASE_DATIVE = 3; + public final static int CASE_ABLATIVE = 4; + public final static int CASE_GENITIVE = 5; + public final static int CASE_VOCATIVE = 6; + public final static int CASE_LOCATIVE = 7; + public final static int CASE_INSTRUMENTAL = 8; + + private List<AbstractTts<? extends AbstractTts<?>>> says = + new ArrayList<AbstractTts<? extends AbstractTts<?>>>(); + Boolean mNoWarningOnFallback = null; + + /** + * Objects deriving from this class can be appended to a Utterance. This class uses generics + * so method from this class can return instances of its child classes, resulting in a better + * API (CRTP pattern). + */ + public static abstract class AbstractTts<C extends AbstractTts<C>> { + + protected Markup mMarkup = new Markup(); + + /** + * Empty constructor. + */ + protected AbstractTts() { + } + + /** + * Construct with Markup. + * @param markup + */ + protected AbstractTts(Markup markup) { + mMarkup = markup; + } + + /** + * Returns the type of this class, e.g. "cardinal" or "measure". + * @return The type. + */ + public String getType() { + return mMarkup.getType(); + } + + /** + * A fallback plain text can be provided, in case the engine does not support this class + * type, or even Markup altogether. + * @param plainText A string with the plain text. + * @return This instance. + */ + @SuppressWarnings("unchecked") + public C setPlainText(String plainText) { + mMarkup.setPlainText(plainText); + return (C) this; + } + + /** + * Returns the plain text (fallback) string. + * @return Plain text string or null if not set. + */ + public String getPlainText() { + return mMarkup.getPlainText(); + } + + /** + * Populates the plainText if not set and builds a Markup instance. + * @return The Markup object describing this instance. + */ + public Markup getMarkup() { + return new Markup(mMarkup); + } + + @SuppressWarnings("unchecked") + protected C setParameter(String key, String value) { + mMarkup.setParameter(key, value); + return (C) this; + } + + protected String getParameter(String key) { + return mMarkup.getParameter(key); + } + + @SuppressWarnings("unchecked") + protected C removeParameter(String key) { + mMarkup.removeParameter(key); + return (C) this; + } + + /** + * Returns a string representation of this instance, can be deserialized to an equal + * Utterance instance. + */ + public String toString() { + return mMarkup.toString(); + } + + /** + * Returns a generated plain text alternative for this instance if this instance isn't + * better representated by the list of it's children. + * @return Best effort plain text representation of this instance, can be null. + */ + public String generatePlainText() { + return null; + } + } + + public static abstract class AbstractTtsSemioticClass<C extends AbstractTtsSemioticClass<C>> + extends AbstractTts<C> { + // Keys. + private static final String KEY_GENDER = "gender"; + private static final String KEY_ANIMACY = "animacy"; + private static final String KEY_MULTIPLICITY = "multiplicity"; + private static final String KEY_CASE = "case"; + + protected AbstractTtsSemioticClass() { + super(); + } + + protected AbstractTtsSemioticClass(Markup markup) { + super(markup); + } + + @SuppressWarnings("unchecked") + public C setGender(int gender) { + if (gender < 0 || gender > 3) { + throw new IllegalArgumentException("Only four types of gender can be set: " + + "unknown, neutral, maculine and female."); + } + if (gender != GENDER_UNKNOWN) { + setParameter(KEY_GENDER, String.valueOf(gender)); + } else { + setParameter(KEY_GENDER, null); + } + return (C) this; + } + + public int getGender() { + String gender = mMarkup.getParameter(KEY_GENDER); + return gender != null ? Integer.valueOf(gender) : GENDER_UNKNOWN; + } + + @SuppressWarnings("unchecked") + public C setAnimacy(int animacy) { + if (animacy < 0 || animacy > 2) { + throw new IllegalArgumentException( + "Only two types of animacy can be set: unknown, animate and inanimate"); + } + if (animacy != ANIMACY_UNKNOWN) { + setParameter(KEY_ANIMACY, String.valueOf(animacy)); + } else { + setParameter(KEY_ANIMACY, null); + } + return (C) this; + } + + public int getAnimacy() { + String animacy = getParameter(KEY_ANIMACY); + return animacy != null ? Integer.valueOf(animacy) : ANIMACY_UNKNOWN; + } + + @SuppressWarnings("unchecked") + public C setMultiplicity(int multiplicity) { + if (multiplicity < 0 || multiplicity > 3) { + throw new IllegalArgumentException( + "Only four types of multiplicity can be set: unknown, single, dual and " + + "plural."); + } + if (multiplicity != MULTIPLICITY_UNKNOWN) { + setParameter(KEY_MULTIPLICITY, String.valueOf(multiplicity)); + } else { + setParameter(KEY_MULTIPLICITY, null); + } + return (C) this; + } + + public int getMultiplicity() { + String multiplicity = mMarkup.getParameter(KEY_MULTIPLICITY); + return multiplicity != null ? Integer.valueOf(multiplicity) : MULTIPLICITY_UNKNOWN; + } + + @SuppressWarnings("unchecked") + public C setCase(int grammaticalCase) { + if (grammaticalCase < 0 || grammaticalCase > 8) { + throw new IllegalArgumentException( + "Only nine types of grammatical case can be set."); + } + if (grammaticalCase != CASE_UNKNOWN) { + setParameter(KEY_CASE, String.valueOf(grammaticalCase)); + } else { + setParameter(KEY_CASE, null); + } + return (C) this; + } + + public int getCase() { + String grammaticalCase = mMarkup.getParameter(KEY_CASE); + return grammaticalCase != null ? Integer.valueOf(grammaticalCase) : CASE_UNKNOWN; + } + } + + /** + * Class that contains regular text, synthesis engine pronounces it using its regular pipeline. + * Parameters: + * <ul> + * <li>Text: the text to synthesize</li> + * </ul> + */ + public static class TtsText extends AbstractTtsSemioticClass<TtsText> { + + // The type of this node. + protected static final String TYPE_TEXT = "text"; + // The text parameter stores the text to be synthesized. + private static final String KEY_TEXT = "text"; + + /** + * Default constructor. + */ + public TtsText() { + mMarkup.setType(TYPE_TEXT); + } + + /** + * Constructor that sets the text to be synthesized. + * @param text The text to be synthesized. + */ + public TtsText(String text) { + this(); + setText(text); + } + + /** + * Constructs a TtsText with the values of the Markup, does not check if the given Markup is + * of the right type. + */ + private TtsText(Markup markup) { + super(markup); + } + + /** + * Sets the text to be synthesized. + * @return This instance. + */ + public TtsText setText(String text) { + setParameter(KEY_TEXT, text); + return this; + } + + /** + * Returns the text to be synthesized. + * @return This instance. + */ + public String getText() { + return getParameter(KEY_TEXT); + } + + /** + * Generates a best effort plain text, in this case simply the text. + */ + @Override + public String generatePlainText() { + return getText(); + } + } + + /** + * Contains a cardinal. + * Parameters: + * <ul> + * <li>integer: the integer to synthesize</li> + * </ul> + */ + public static class TtsCardinal extends AbstractTtsSemioticClass<TtsCardinal> { + + // The type of this node. + protected static final String TYPE_CARDINAL = "cardinal"; + // The parameter integer stores the integer to synthesize. + private static final String KEY_INTEGER = "integer"; + + /** + * Default constructor. + */ + public TtsCardinal() { + mMarkup.setType(TYPE_CARDINAL); + } + + /** + * Constructor that sets the integer to be synthesized. + */ + public TtsCardinal(int integer) { + this(); + setInteger(integer); + } + + /** + * Constructor that sets the integer to be synthesized. + */ + public TtsCardinal(String integer) { + this(); + setInteger(integer); + } + + /** + * Constructs a TtsText with the values of the Markup. + * Does not check if the given Markup is of the right type. + */ + private TtsCardinal(Markup markup) { + super(markup); + } + + /** + * Sets the integer. + * @return This instance. + */ + public TtsCardinal setInteger(int integer) { + return setInteger(String.valueOf(integer)); + } + + /** + * Sets the integer. + * @param integer A non-empty string of digits with an optional '-' in front. + * @return This instance. + */ + public TtsCardinal setInteger(String integer) { + if (!integer.matches("-?\\d+")) { + throw new IllegalArgumentException("Expected a cardinal: \"" + integer + "\""); + } + setParameter(KEY_INTEGER, integer); + return this; + } + + /** + * Returns the integer parameter. + */ + public String getInteger() { + return getParameter(KEY_INTEGER); + } + + /** + * Generates a best effort plain text, in this case simply the integer. + */ + @Override + public String generatePlainText() { + return getInteger(); + } + } + + /** + * Default constructor. + */ + public Utterance() {} + + /** + * Returns the plain text of a given Markup if it was set; if it's not set, recursively call the + * this same method on its children. + */ + private String constructPlainText(Markup m) { + StringBuilder plainText = new StringBuilder(); + if (m.getPlainText() != null) { + plainText.append(m.getPlainText()); + } else { + for (Markup nestedMarkup : m.getNestedMarkups()) { + String nestedPlainText = constructPlainText(nestedMarkup); + if (!nestedPlainText.isEmpty()) { + if (plainText.length() != 0) { + plainText.append(" "); + } + plainText.append(nestedPlainText); + } + } + } + return plainText.toString(); + } + + /** + * Creates a Markup instance with auto generated plain texts for the relevant nodes, in case the + * user has not provided one already. + * @return A Markup instance representing this utterance. + */ + public Markup createMarkup() { + Markup markup = new Markup(TYPE_UTTERANCE); + StringBuilder plainText = new StringBuilder(); + for (AbstractTts<? extends AbstractTts<?>> say : says) { + // Get a copy of this markup, and generate a plaintext for it if is not set. + Markup sayMarkup = say.getMarkup(); + if (sayMarkup.getPlainText() == null) { + sayMarkup.setPlainText(say.generatePlainText()); + } + if (plainText.length() != 0) { + plainText.append(" "); + } + plainText.append(constructPlainText(sayMarkup)); + markup.addNestedMarkup(sayMarkup); + } + if (mNoWarningOnFallback != null) { + markup.setParameter(KEY_NO_WARNING_ON_FALLBACK, + mNoWarningOnFallback ? "true" : "false"); + } + markup.setPlainText(plainText.toString()); + return markup; + } + + /** + * Appends an element to this Utterance instance. + * @return this instance + */ + public Utterance append(AbstractTts<? extends AbstractTts<?>> say) { + says.add(say); + return this; + } + + private Utterance append(Markup markup) { + if (markup.getType().equals(TtsText.TYPE_TEXT)) { + append(new TtsText(markup)); + } else if (markup.getType().equals(TtsCardinal.TYPE_CARDINAL)) { + append(new TtsCardinal(markup)); + } else { + // Unknown node, a class we don't know about. + if (markup.getPlainText() != null) { + append(new TtsText(markup.getPlainText())); + } else { + // No plainText specified; add its children + // seperately. In case of a new prosody node, + // we would still verbalize it correctly. + for (Markup nested : markup.getNestedMarkups()) { + append(nested); + } + } + } + return this; + } + + /** + * Returns a string representation of this Utterance instance. Can be deserialized back to an + * Utterance instance with utteranceFromString(). Can be used to store utterances to be used + * at a later time. + */ + public String toString() { + String out = "type: \"" + TYPE_UTTERANCE + "\""; + if (mNoWarningOnFallback != null) { + out += " no_warning_on_fallback: \"" + (mNoWarningOnFallback ? "true" : "false") + "\""; + } + for (AbstractTts<? extends AbstractTts<?>> say : says) { + out += " markup { " + say.getMarkup().toString() + " }"; + } + return out; + } + + /** + * Returns an Utterance instance from the string representation generated by toString(). + * @param string The string representation generated by toString(). + * @return The new Utterance instance. + * @throws {@link IllegalArgumentException} if the input cannot be correctly parsed. + */ + static public Utterance utteranceFromString(String string) throws IllegalArgumentException { + Utterance utterance = new Utterance(); + Markup markup = Markup.markupFromString(string); + if (!markup.getType().equals(TYPE_UTTERANCE)) { + throw new IllegalArgumentException("Top level markup should be of type \"" + + TYPE_UTTERANCE + "\", but was of type \"" + + markup.getType() + "\".") ; + } + for (Markup nestedMarkup : markup.getNestedMarkups()) { + utterance.append(nestedMarkup); + } + return utterance; + } + + /** + * Appends a new TtsText with the given text. + * @param text The text to synthesize. + * @return This instance. + */ + public Utterance append(String text) { + return append(new TtsText(text)); + } + + /** + * Appends a TtsCardinal representing the given number. + * @param integer The integer to synthesize. + * @return this + */ + public Utterance append(int integer) { + return append(new TtsCardinal(integer)); + } + + /** + * Returns the n'th element in this Utterance. + * @param i The index. + * @return The n'th element in this Utterance. + * @throws {@link IndexOutOfBoundsException} - if i < 0 || i >= size() + */ + public AbstractTts<? extends AbstractTts<?>> get(int i) { + return says.get(i); + } + + /** + * Returns the number of elements in this Utterance. + * @return The number of elements in this Utterance. + */ + public int size() { + return says.size(); + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !(o instanceof Utterance) ) return false; + Utterance utt = (Utterance) o; + + if (says.size() != utt.says.size()) { + return false; + } + + for (int i = 0; i < says.size(); i++) { + if (!says.get(i).getMarkup().equals(utt.says.get(i).getMarkup())) { + return false; + } + } + return true; + } + + /** + * Can be set to true or false, true indicating that the user provided only a string to the API, + * at which the system will not issue a warning if the synthesizer falls back onto the plain + * text when the synthesizer does not support Markup. + */ + public Utterance setNoWarningOnFallback(boolean noWarning) { + mNoWarningOnFallback = noWarning; + return this; + } +} diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 424d860..5056097 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -75,22 +75,10 @@ class GLES20Canvas extends HardwareCanvas { // Constructors /////////////////////////////////////////////////////////////////////////// - /** - * Creates a canvas to render directly on screen. - */ - GLES20Canvas(boolean translucent) { - this(false, translucent); - } - - protected GLES20Canvas(boolean record, boolean translucent) { - mOpaque = !translucent; - - if (record) { - mRenderer = nCreateDisplayListRenderer(); - } else { - mRenderer = nCreateRenderer(); - } - + // TODO: Merge with GLES20RecordingCanvas + protected GLES20Canvas() { + mOpaque = false; + mRenderer = nCreateDisplayListRenderer(); setupFinalizer(); } @@ -102,7 +90,6 @@ class GLES20Canvas extends HardwareCanvas { } } - private static native long nCreateRenderer(); private static native long nCreateDisplayListRenderer(); private static native void nResetDisplayListRenderer(long renderer); private static native void nDestroyRenderer(long renderer); @@ -131,36 +118,6 @@ class GLES20Canvas extends HardwareCanvas { private static native void nSetProperty(String name, String value); /////////////////////////////////////////////////////////////////////////// - // Hardware layers - /////////////////////////////////////////////////////////////////////////// - - @Override - void pushLayerUpdate(HardwareLayer layer) { - nPushLayerUpdate(mRenderer, layer.getLayer()); - } - - @Override - void cancelLayerUpdate(HardwareLayer layer) { - nCancelLayerUpdate(mRenderer, layer.getLayer()); - } - - @Override - void flushLayerUpdates() { - nFlushLayerUpdates(mRenderer); - } - - @Override - void clearLayerUpdates() { - nClearLayerUpdates(mRenderer); - } - - static native boolean nCopyLayer(long layerId, long bitmap); - private static native void nClearLayerUpdates(long renderer); - private static native void nFlushLayerUpdates(long renderer); - private static native void nPushLayerUpdate(long renderer, long layer); - private static native void nCancelLayerUpdate(long renderer, long layer); - - /////////////////////////////////////////////////////////////////////////// // Canvas management /////////////////////////////////////////////////////////////////////////// @@ -234,20 +191,6 @@ class GLES20Canvas extends HardwareCanvas { private static native void nFinish(long renderer); - /** - * Returns the size of the stencil buffer required by the underlying - * implementation. - * - * @return The minimum number of bits the stencil buffer must. Always >= 0. - * - * @hide - */ - public static int getStencilSize() { - return nGetStencilSize(); - } - - private static native int nGetStencilSize(); - /////////////////////////////////////////////////////////////////////////// // Functor /////////////////////////////////////////////////////////////////////////// @@ -284,49 +227,6 @@ class GLES20Canvas extends HardwareCanvas { */ static final int FLUSH_CACHES_FULL = 2; - /** - * Flush caches to reclaim as much memory as possible. The amount of memory - * to reclaim is indicate by the level parameter. - * - * The level can be one of {@link #FLUSH_CACHES_MODERATE} or - * {@link #FLUSH_CACHES_FULL}. - * - * @param level Hint about the amount of memory to reclaim - */ - static void flushCaches(int level) { - nFlushCaches(level); - } - - private static native void nFlushCaches(int level); - - /** - * Release all resources associated with the underlying caches. This should - * only be called after a full flushCaches(). - * - * @hide - */ - static void terminateCaches() { - nTerminateCaches(); - } - - private static native void nTerminateCaches(); - - static boolean initCaches() { - return nInitCaches(); - } - - private static native boolean nInitCaches(); - - /////////////////////////////////////////////////////////////////////////// - // Atlas - /////////////////////////////////////////////////////////////////////////// - - static void initAtlas(GraphicBuffer buffer, long[] map) { - nInitAtlas(buffer, map, map.length); - } - - private static native void nInitAtlas(GraphicBuffer buffer, long[] map, int count); - /////////////////////////////////////////////////////////////////////////// // Display list /////////////////////////////////////////////////////////////////////////// @@ -899,12 +799,6 @@ class GLES20Canvas extends HardwareCanvas { private static native void nDrawPath(long renderer, long path, long paint); private static native void nDrawRects(long renderer, long region, long paint); - void drawRects(float[] rects, int count, Paint paint) { - nDrawRects(mRenderer, rects, count, paint.mNativePaint); - } - - private static native void nDrawRects(long renderer, float[] rects, int count, long paint); - @Override public void drawPicture(Picture picture) { if (picture.createdFromStream) { diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java index a94ec3a..b2961e5 100644 --- a/core/java/android/view/GLES20RecordingCanvas.java +++ b/core/java/android/view/GLES20RecordingCanvas.java @@ -36,7 +36,7 @@ class GLES20RecordingCanvas extends GLES20Canvas { RenderNode mNode; private GLES20RecordingCanvas() { - super(true, true); + super(); } static GLES20RecordingCanvas obtain(@NonNull RenderNode node) { diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java deleted file mode 100644 index f1163e2..0000000 --- a/core/java/android/view/GLRenderer.java +++ /dev/null @@ -1,1521 +0,0 @@ -/* - * Copyright (C) 2013 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.view; - -import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE; -import static javax.microedition.khronos.egl.EGL10.EGL_BAD_NATIVE_WINDOW; -import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE; -import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT; -import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY; -import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE; -import static javax.microedition.khronos.egl.EGL10.EGL_DRAW; -import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE; -import static javax.microedition.khronos.egl.EGL10.EGL_HEIGHT; -import static javax.microedition.khronos.egl.EGL10.EGL_NONE; -import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT; -import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY; -import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE; -import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE; -import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE; -import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES; -import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS; -import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE; -import static javax.microedition.khronos.egl.EGL10.EGL_SUCCESS; -import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE; -import static javax.microedition.khronos.egl.EGL10.EGL_WIDTH; -import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT; - -import android.content.ComponentCallbacks2; -import android.graphics.Bitmap; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.SurfaceTexture; -import android.opengl.EGL14; -import android.opengl.GLUtils; -import android.opengl.ManagedEGLContext; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.Trace; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.Surface.OutOfResourcesException; - -import com.google.android.gles_jni.EGLImpl; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.locks.ReentrantLock; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGL11; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; -import javax.microedition.khronos.opengles.GL; - -/** - * Hardware renderer using OpenGL - * - * @hide - */ -public class GLRenderer extends HardwareRenderer { - static final int SURFACE_STATE_ERROR = 0; - static final int SURFACE_STATE_SUCCESS = 1; - static final int SURFACE_STATE_UPDATED = 2; - - static final int FUNCTOR_PROCESS_DELAY = 4; - - /** - * Number of frames to profile. - */ - private static final int PROFILE_MAX_FRAMES = 128; - - /** - * Number of floats per profiled frame. - */ - private static final int PROFILE_FRAME_DATA_COUNT = 3; - - private static final int PROFILE_DRAW_MARGIN = 0; - private static final int PROFILE_DRAW_WIDTH = 3; - private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 }; - private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d; - private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d; - private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2; - private static final int PROFILE_DRAW_DP_PER_MS = 7; - - private static final String[] VISUALIZERS = { - PROFILE_PROPERTY_VISUALIZE_BARS, - }; - - private static final String[] OVERDRAW = { - OVERDRAW_PROPERTY_SHOW, - }; - private static final int GL_VERSION = 2; - - static EGL10 sEgl; - static EGLDisplay sEglDisplay; - static EGLConfig sEglConfig; - static final Object[] sEglLock = new Object[0]; - int mWidth = -1, mHeight = -1; - - static final ThreadLocal<ManagedEGLContext> sEglContextStorage - = new ThreadLocal<ManagedEGLContext>(); - - EGLContext mEglContext; - Thread mEglThread; - - EGLSurface mEglSurface; - - GL mGl; - HardwareCanvas mCanvas; - - String mName; - - long mFrameCount; - Paint mDebugPaint; - - static boolean sDirtyRegions; - static final boolean sDirtyRegionsRequested; - static { - String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); - //noinspection PointlessBooleanExpression,ConstantConditions - sDirtyRegions = "true".equalsIgnoreCase(dirtyProperty); - sDirtyRegionsRequested = sDirtyRegions; - } - - boolean mDirtyRegionsEnabled; - boolean mUpdateDirtyRegions; - - boolean mProfileEnabled; - int mProfileVisualizerType = -1; - float[] mProfileData; - ReentrantLock mProfileLock; - int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; - - GraphDataProvider mDebugDataProvider; - float[][] mProfileShapes; - Paint mProfilePaint; - - boolean mDebugDirtyRegions; - int mDebugOverdraw = -1; - - final boolean mTranslucent; - - private boolean mDestroyed; - - private final Rect mRedrawClip = new Rect(); - - private final int[] mSurfaceSize = new int[2]; - - private long mDrawDelta = Long.MAX_VALUE; - - private GLES20Canvas mGlCanvas; - - private DisplayMetrics mDisplayMetrics; - - private static EGLSurface sPbuffer; - private static final Object[] sPbufferLock = new Object[0]; - - private List<HardwareLayer> mLayerUpdates = new ArrayList<HardwareLayer>(); - - private static class GLRendererEglContext extends ManagedEGLContext { - final Handler mHandler = new Handler(); - - public GLRendererEglContext(EGLContext context) { - super(context); - } - - @Override - public void onTerminate(final EGLContext eglContext) { - // Make sure we do this on the correct thread. - if (mHandler.getLooper() != Looper.myLooper()) { - mHandler.post(new Runnable() { - @Override - public void run() { - onTerminate(eglContext); - } - }); - return; - } - - synchronized (sEglLock) { - if (sEgl == null) return; - - if (EGLImpl.getInitCount(sEglDisplay) == 1) { - usePbufferSurface(eglContext); - GLES20Canvas.terminateCaches(); - - sEgl.eglDestroyContext(sEglDisplay, eglContext); - sEglContextStorage.set(null); - sEglContextStorage.remove(); - - sEgl.eglDestroySurface(sEglDisplay, sPbuffer); - sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, - EGL_NO_SURFACE, EGL_NO_CONTEXT); - - sEgl.eglReleaseThread(); - sEgl.eglTerminate(sEglDisplay); - - sEgl = null; - sEglDisplay = null; - sEglConfig = null; - sPbuffer = null; - } - } - } - } - - HardwareCanvas createCanvas() { - return mGlCanvas = new GLES20Canvas(mTranslucent); - } - - ManagedEGLContext createManagedContext(EGLContext eglContext) { - return new GLRendererEglContext(mEglContext); - } - - int[] getConfig(boolean dirtyRegions) { - //noinspection PointlessBooleanExpression,ConstantConditions - final int stencilSize = GLES20Canvas.getStencilSize(); - final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; - - return new int[] { - EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 0, - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_STENCIL_SIZE, stencilSize, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, - EGL_NONE - }; - } - - void initCaches() { - if (GLES20Canvas.initCaches()) { - // Caches were (re)initialized, rebind atlas - initAtlas(); - } - } - - void initAtlas() { - IBinder binder = ServiceManager.getService("assetatlas"); - if (binder == null) return; - - IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder); - try { - if (atlas.isCompatible(android.os.Process.myPpid())) { - GraphicBuffer buffer = atlas.getBuffer(); - if (buffer != null) { - long[] map = atlas.getMap(); - if (map != null) { - GLES20Canvas.initAtlas(buffer, map); - } - // If IAssetAtlas is not the same class as the IBinder - // we are using a remote service and we can safely - // destroy the graphic buffer - if (atlas.getClass() != binder.getClass()) { - buffer.destroy(); - } - } - } - } catch (RemoteException e) { - Log.w(LOG_TAG, "Could not acquire atlas", e); - } - } - - boolean canDraw() { - return mGl != null && mCanvas != null && mGlCanvas != null; - } - - int onPreDraw(Rect dirty) { - return mGlCanvas.onPreDraw(dirty); - } - - void onPostDraw() { - mGlCanvas.onPostDraw(); - } - - void drawProfileData(View.AttachInfo attachInfo) { - if (mDebugDataProvider != null) { - final GraphDataProvider provider = mDebugDataProvider; - initProfileDrawData(attachInfo, provider); - - final int height = provider.getVerticalUnitSize(); - final int margin = provider.getHorizontaUnitMargin(); - final int width = provider.getHorizontalUnitSize(); - - int x = 0; - int count = 0; - int current = 0; - - final float[] data = provider.getData(); - final int elementCount = provider.getElementCount(); - final int graphType = provider.getGraphType(); - - int totalCount = provider.getFrameCount() * elementCount; - if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) { - totalCount -= elementCount; - } - - for (int i = 0; i < totalCount; i += elementCount) { - if (data[i] < 0.0f) break; - - int index = count * 4; - if (i == provider.getCurrentFrame() * elementCount) current = index; - - x += margin; - int x2 = x + width; - - int y2 = mHeight; - int y1 = (int) (y2 - data[i] * height); - - switch (graphType) { - case GraphDataProvider.GRAPH_TYPE_BARS: { - for (int j = 0; j < elementCount; j++) { - //noinspection MismatchedReadAndWriteOfArray - final float[] r = mProfileShapes[j]; - r[index] = x; - r[index + 1] = y1; - r[index + 2] = x2; - r[index + 3] = y2; - - y2 = y1; - if (j < elementCount - 1) { - y1 = (int) (y2 - data[i + j + 1] * height); - } - } - } break; - case GraphDataProvider.GRAPH_TYPE_LINES: { - for (int j = 0; j < elementCount; j++) { - //noinspection MismatchedReadAndWriteOfArray - final float[] r = mProfileShapes[j]; - r[index] = (x + x2) * 0.5f; - r[index + 1] = index == 0 ? y1 : r[index - 1]; - r[index + 2] = r[index] + width; - r[index + 3] = y1; - - y2 = y1; - if (j < elementCount - 1) { - y1 = (int) (y2 - data[i + j + 1] * height); - } - } - } break; - } - - - x += width; - count++; - } - - x += margin; - - drawGraph(graphType, count); - drawCurrentFrame(graphType, current); - drawThreshold(x, height); - } - } - - private void drawGraph(int graphType, int count) { - for (int i = 0; i < mProfileShapes.length; i++) { - mDebugDataProvider.setupGraphPaint(mProfilePaint, i); - switch (graphType) { - case GraphDataProvider.GRAPH_TYPE_BARS: - mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint); - break; - case GraphDataProvider.GRAPH_TYPE_LINES: - mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint); - break; - } - } - } - - private void drawCurrentFrame(int graphType, int index) { - if (index >= 0) { - mDebugDataProvider.setupCurrentFramePaint(mProfilePaint); - switch (graphType) { - case GraphDataProvider.GRAPH_TYPE_BARS: - mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1], - mProfileShapes[2][index + 2], mProfileShapes[0][index + 3], - mProfilePaint); - break; - case GraphDataProvider.GRAPH_TYPE_LINES: - mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1], - mProfileShapes[2][index], mHeight, mProfilePaint); - break; - } - } - } - - private void drawThreshold(int x, int height) { - float threshold = mDebugDataProvider.getThreshold(); - if (threshold > 0.0f) { - mDebugDataProvider.setupThresholdPaint(mProfilePaint); - int y = (int) (mHeight - threshold * height); - mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint); - } - } - - private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) { - if (mProfileShapes == null) { - final int elementCount = provider.getElementCount(); - final int frameCount = provider.getFrameCount(); - - mProfileShapes = new float[elementCount][]; - for (int i = 0; i < elementCount; i++) { - mProfileShapes[i] = new float[frameCount * 4]; - } - - mProfilePaint = new Paint(); - } - - mProfilePaint.reset(); - if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) { - mProfilePaint.setAntiAlias(true); - } - - if (mDisplayMetrics == null) { - mDisplayMetrics = new DisplayMetrics(); - } - - attachInfo.mDisplay.getMetrics(mDisplayMetrics); - provider.prepare(mDisplayMetrics); - } - - @Override - void destroy(boolean full) { - try { - if (full && mCanvas != null) { - mCanvas = null; - } - - if (!isEnabled() || mDestroyed) { - setEnabled(false); - return; - } - - destroySurface(); - setEnabled(false); - - mDestroyed = true; - mGl = null; - } finally { - if (full && mGlCanvas != null) { - mGlCanvas = null; - } - } - } - - @Override - void pushLayerUpdate(HardwareLayer layer) { - mLayerUpdates.add(layer); - } - - @Override - void flushLayerUpdates() { - if (validate()) { - flushLayerChanges(); - mGlCanvas.flushLayerUpdates(); - } - } - - @Override - HardwareLayer createTextureLayer() { - validate(); - return HardwareLayer.createTextureLayer(this); - } - - @Override - public HardwareLayer createDisplayListLayer(int width, int height) { - validate(); - return HardwareLayer.createDisplayListLayer(this, width, height); - } - - boolean hasContext() { - return sEgl != null && mEglContext != null - && mEglContext.equals(sEgl.eglGetCurrentContext()); - } - - @Override - void onLayerDestroyed(HardwareLayer layer) { - if (mGlCanvas != null) { - mGlCanvas.cancelLayerUpdate(layer); - } - mLayerUpdates.remove(layer); - } - - @Override - public SurfaceTexture createSurfaceTexture(HardwareLayer layer) { - return layer.createSurfaceTexture(); - } - - @Override - boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap) { - if (!validate()) { - throw new IllegalStateException("Could not acquire hardware rendering context"); - } - layer.flushChanges(); - return GLES20Canvas.nCopyLayer(layer.getLayer(), bitmap.mNativeBitmap); - } - - @Override - boolean safelyRun(Runnable action) { - boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR; - - if (needsContext) { - GLRendererEglContext managedContext = - (GLRendererEglContext) sEglContextStorage.get(); - if (managedContext == null) return false; - usePbufferSurface(managedContext.getContext()); - } - - try { - action.run(); - } finally { - if (needsContext) { - sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, - EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - } - - return true; - } - - @Override - void invokeFunctor(long functor, boolean waitForCompletion) { - boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR; - boolean hasContext = !needsContext; - - if (needsContext) { - GLRendererEglContext managedContext = - (GLRendererEglContext) sEglContextStorage.get(); - if (managedContext != null) { - usePbufferSurface(managedContext.getContext()); - hasContext = true; - } - } - - try { - nInvokeFunctor(functor, hasContext); - } finally { - if (needsContext) { - sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, - EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - } - } - - private static native void nInvokeFunctor(long functor, boolean hasContext); - - @Override - void destroyHardwareResources(final View view) { - if (view != null) { - safelyRun(new Runnable() { - @Override - public void run() { - if (mCanvas != null) { - mCanvas.clearLayerUpdates(); - } - destroyResources(view); - GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); - } - }); - } - } - - private static void destroyResources(View view) { - view.destroyHardwareResources(); - - if (view instanceof ViewGroup) { - ViewGroup group = (ViewGroup) view; - - int count = group.getChildCount(); - for (int i = 0; i < count; i++) { - destroyResources(group.getChildAt(i)); - } - } - } - - static void startTrimMemory(int level) { - if (sEgl == null || sEglConfig == null) return; - - GLRendererEglContext managedContext = - (GLRendererEglContext) sEglContextStorage.get(); - // We do not have OpenGL objects - if (managedContext == null) { - return; - } else { - usePbufferSurface(managedContext.getContext()); - } - - if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { - GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL); - } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { - GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE); - } - } - - static void endTrimMemory() { - if (sEgl != null && sEglDisplay != null) { - sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - } - - private static void usePbufferSurface(EGLContext eglContext) { - synchronized (sPbufferLock) { - // Create a temporary 1x1 pbuffer so we have a context - // to clear our OpenGL objects - if (sPbuffer == null) { - sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] { - EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE - }); - } - } - sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); - } - - GLRenderer(boolean translucent) { - mTranslucent = translucent; - - loadSystemProperties(); - } - - @Override - void setOpaque(boolean opaque) { - // Not supported - } - - @Override - boolean loadSystemProperties() { - boolean value; - boolean changed = false; - - String profiling = SystemProperties.get(PROFILE_PROPERTY); - int graphType = search(VISUALIZERS, profiling); - value = graphType >= 0; - - if (graphType != mProfileVisualizerType) { - changed = true; - mProfileVisualizerType = graphType; - - mProfileShapes = null; - mProfilePaint = null; - - if (value) { - mDebugDataProvider = new GraphDataProvider(graphType); - } else { - mDebugDataProvider = null; - } - } - - // If on-screen profiling is not enabled, we need to check whether - // console profiling only is enabled - if (!value) { - value = Boolean.parseBoolean(profiling); - } - - if (value != mProfileEnabled) { - changed = true; - mProfileEnabled = value; - - if (mProfileEnabled) { - Log.d(LOG_TAG, "Profiling hardware renderer"); - - int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY, - PROFILE_MAX_FRAMES); - mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT]; - for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { - mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; - } - - mProfileLock = new ReentrantLock(); - } else { - mProfileData = null; - mProfileLock = null; - mProfileVisualizerType = -1; - } - - mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT; - } - - value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false); - if (value != mDebugDirtyRegions) { - changed = true; - mDebugDirtyRegions = value; - - if (mDebugDirtyRegions) { - Log.d(LOG_TAG, "Debugging dirty regions"); - } - } - - String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY); - int debugOverdraw = search(OVERDRAW, overdraw); - if (debugOverdraw != mDebugOverdraw) { - changed = true; - mDebugOverdraw = debugOverdraw; - } - - if (loadProperties()) { - changed = true; - } - - return changed; - } - - private static int search(String[] values, String value) { - for (int i = 0; i < values.length; i++) { - if (values[i].equals(value)) return i; - } - return -1; - } - - @Override - void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) { - if (mProfileEnabled) { - pw.printf("\n\tDraw\tProcess\tExecute\n"); - - mProfileLock.lock(); - try { - for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) { - if (mProfileData[i] < 0) { - break; - } - pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1], - mProfileData[i + 2]); - mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1; - } - mProfileCurrentFrame = mProfileData.length; - } finally { - mProfileLock.unlock(); - } - } - } - - /** - * Indicates whether this renderer instance can track and update dirty regions. - */ - boolean hasDirtyRegions() { - return mDirtyRegionsEnabled; - } - - /** - * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)} - * is invoked and the requested flag is turned off. The error code is - * also logged as a warning. - */ - void checkEglErrors() { - if (isEnabled()) { - checkEglErrorsForced(); - } - } - - private void checkEglErrorsForced() { - int error = sEgl.eglGetError(); - if (error != EGL_SUCCESS) { - // something bad has happened revert to - // normal rendering. - Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error)); - fallback(error != EGL11.EGL_CONTEXT_LOST); - } - } - - private void fallback(boolean fallback) { - destroy(true); - if (fallback) { - // we'll try again if it was context lost - setRequested(false); - Log.w(LOG_TAG, "Mountain View, we've had a problem here. " - + "Switching back to software rendering."); - } - } - - @Override - boolean initialize(Surface surface) throws OutOfResourcesException { - if (isRequested() && !isEnabled()) { - boolean contextCreated = initializeEgl(); - mGl = createEglSurface(surface); - mDestroyed = false; - - if (mGl != null) { - int err = sEgl.eglGetError(); - if (err != EGL_SUCCESS) { - destroy(true); - setRequested(false); - } else { - if (mCanvas == null) { - mCanvas = createCanvas(); - } - setEnabled(true); - - if (contextCreated) { - initAtlas(); - } - } - - return mCanvas != null; - } - } - return false; - } - - @Override - void updateSurface(Surface surface) throws OutOfResourcesException { - if (isRequested() && isEnabled()) { - createEglSurface(surface); - } - } - - @Override - void pauseSurface(Surface surface) { - // No-op - } - - boolean initializeEgl() { - synchronized (sEglLock) { - if (sEgl == null && sEglConfig == null) { - sEgl = (EGL10) EGLContext.getEGL(); - - // Get to the default display. - sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY); - - if (sEglDisplay == EGL_NO_DISPLAY) { - throw new RuntimeException("eglGetDisplay failed " - + GLUtils.getEGLErrorString(sEgl.eglGetError())); - } - - // We can now initialize EGL for that display - int[] version = new int[2]; - if (!sEgl.eglInitialize(sEglDisplay, version)) { - throw new RuntimeException("eglInitialize failed " + - GLUtils.getEGLErrorString(sEgl.eglGetError())); - } - - checkEglErrorsForced(); - - sEglConfig = loadEglConfig(); - } - } - - ManagedEGLContext managedContext = sEglContextStorage.get(); - mEglContext = managedContext != null ? managedContext.getContext() : null; - mEglThread = Thread.currentThread(); - - if (mEglContext == null) { - mEglContext = createContext(sEgl, sEglDisplay, sEglConfig); - sEglContextStorage.set(createManagedContext(mEglContext)); - return true; - } - - return false; - } - - private EGLConfig loadEglConfig() { - EGLConfig eglConfig = chooseEglConfig(); - if (eglConfig == null) { - // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without - if (sDirtyRegions) { - sDirtyRegions = false; - eglConfig = chooseEglConfig(); - if (eglConfig == null) { - throw new RuntimeException("eglConfig not initialized"); - } - } else { - throw new RuntimeException("eglConfig not initialized"); - } - } - return eglConfig; - } - - private EGLConfig chooseEglConfig() { - EGLConfig[] configs = new EGLConfig[1]; - int[] configsCount = new int[1]; - int[] configSpec = getConfig(sDirtyRegions); - - // Debug - final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, ""); - if ("all".equalsIgnoreCase(debug)) { - sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount); - - EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]]; - sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs, - configsCount[0], configsCount); - - for (EGLConfig config : debugConfigs) { - printConfig(config); - } - } - - if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) { - throw new IllegalArgumentException("eglChooseConfig failed " + - GLUtils.getEGLErrorString(sEgl.eglGetError())); - } else if (configsCount[0] > 0) { - if ("choice".equalsIgnoreCase(debug)) { - printConfig(configs[0]); - } - return configs[0]; - } - - return null; - } - - private static void printConfig(EGLConfig config) { - int[] value = new int[1]; - - Log.d(LOG_TAG, "EGL configuration " + config + ":"); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value); - Log.d(LOG_TAG, " RED_SIZE = " + value[0]); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value); - Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value); - Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value); - Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value); - Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value); - Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value); - Log.d(LOG_TAG, " SAMPLE_BUFFERS = " + value[0]); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value); - Log.d(LOG_TAG, " SAMPLES = " + value[0]); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value); - Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0])); - - sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value); - Log.d(LOG_TAG, " CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0])); - } - - GL createEglSurface(Surface surface) throws OutOfResourcesException { - // Check preconditions. - if (sEgl == null) { - throw new RuntimeException("egl not initialized"); - } - if (sEglDisplay == null) { - throw new RuntimeException("eglDisplay not initialized"); - } - if (sEglConfig == null) { - throw new RuntimeException("eglConfig not initialized"); - } - if (Thread.currentThread() != mEglThread) { - throw new IllegalStateException("HardwareRenderer cannot be used " - + "from multiple threads"); - } - - // In case we need to destroy an existing surface - destroySurface(); - - // Create an EGL surface we can render into. - if (!createSurface(surface)) { - return null; - } - - initCaches(); - - return mEglContext.getGL(); - } - - private void enableDirtyRegions() { - // If mDirtyRegions is set, this means we have an EGL configuration - // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set - if (sDirtyRegions) { - if (!(mDirtyRegionsEnabled = preserveBackBuffer())) { - Log.w(LOG_TAG, "Backbuffer cannot be preserved"); - } - } else if (sDirtyRegionsRequested) { - // If mDirtyRegions is not set, our EGL configuration does not - // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default - // swap behavior might be EGL_BUFFER_PRESERVED, which means we - // want to set mDirtyRegions. We try to do this only if dirty - // regions were initially requested as part of the device - // configuration (see RENDER_DIRTY_REGIONS) - mDirtyRegionsEnabled = isBackBufferPreserved(); - } - } - - EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { - final int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, GL_VERSION, EGL_NONE }; - - EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, - attribs); - if (context == null || context == EGL_NO_CONTEXT) { - //noinspection ConstantConditions - throw new IllegalStateException( - "Could not create an EGL context. eglCreateContext failed with error: " + - GLUtils.getEGLErrorString(sEgl.eglGetError())); - } - - return context; - } - - void destroySurface() { - if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { - if (mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) { - sEgl.eglMakeCurrent(sEglDisplay, - EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } - sEgl.eglDestroySurface(sEglDisplay, mEglSurface); - mEglSurface = null; - } - } - - @Override - void invalidate(Surface surface) { - // Cancels any existing buffer to ensure we'll get a buffer - // of the right size before we call eglSwapBuffers - sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { - sEgl.eglDestroySurface(sEglDisplay, mEglSurface); - mEglSurface = null; - setEnabled(false); - } - - if (surface.isValid()) { - if (!createSurface(surface)) { - return; - } - - mUpdateDirtyRegions = true; - - if (mCanvas != null) { - setEnabled(true); - } - } - } - - private boolean createSurface(Surface surface) { - mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null); - - if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { - int error = sEgl.eglGetError(); - if (error == EGL_BAD_NATIVE_WINDOW) { - Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); - return false; - } - throw new RuntimeException("createWindowSurface failed " - + GLUtils.getEGLErrorString(error)); - } - - if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { - throw new IllegalStateException("eglMakeCurrent failed " + - GLUtils.getEGLErrorString(sEgl.eglGetError())); - } - - enableDirtyRegions(); - - return true; - } - - boolean validate() { - return checkRenderContext() != SURFACE_STATE_ERROR; - } - - @Override - void setup(int width, int height, float lightX, float lightY, float lightZ, float lightRadius) { - if (validate()) { - mCanvas.setViewport(width, height); - mCanvas.initializeLight(lightX, lightY, lightZ, lightRadius); - mWidth = width; - mHeight = height; - } - } - - @Override - int getWidth() { - return mWidth; - } - - @Override - int getHeight() { - return mHeight; - } - - @Override - void setName(String name) { - mName = name; - } - - @Override - void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, - Rect dirty) { - if (canDraw()) { - if (!hasDirtyRegions()) { - dirty = null; - } - attachInfo.mIgnoreDirtyState = true; - attachInfo.mDrawingTime = SystemClock.uptimeMillis(); - - view.mPrivateFlags |= View.PFLAG_DRAWN; - - // We are already on the correct thread - final int surfaceState = checkRenderContextUnsafe(); - if (surfaceState != SURFACE_STATE_ERROR) { - HardwareCanvas canvas = mCanvas; - - if (mProfileEnabled) { - mProfileLock.lock(); - } - - dirty = beginFrame(canvas, dirty, surfaceState); - - RenderNode displayList = buildDisplayList(view, canvas); - - flushLayerChanges(); - - // buildDisplayList() calls into user code which can cause - // an eglMakeCurrent to happen with a different surface/context. - // We must therefore check again here. - if (checkRenderContextUnsafe() == SURFACE_STATE_ERROR) { - return; - } - - int saveCount = 0; - int status = RenderNode.STATUS_DONE; - - long start = getSystemTime(); - try { - status = prepareFrame(dirty); - - saveCount = canvas.save(); - callbacks.onHardwarePreDraw(canvas); - - if (displayList != null) { - status |= drawDisplayList(canvas, displayList, status); - } else { - // Shouldn't reach here - view.draw(canvas); - } - } catch (Exception e) { - Log.e(LOG_TAG, "An error has occurred while drawing:", e); - } finally { - callbacks.onHardwarePostDraw(canvas); - canvas.restoreToCount(saveCount); - view.mRecreateDisplayList = false; - - mDrawDelta = getSystemTime() - start; - - if (mDrawDelta > 0) { - mFrameCount++; - - debugDirtyRegions(dirty, canvas); - drawProfileData(attachInfo); - } - } - - onPostDraw(); - - swapBuffers(status); - - if (mProfileEnabled) { - mProfileLock.unlock(); - } - - attachInfo.mIgnoreDirtyState = false; - } - } - } - - private void flushLayerChanges() { - // Loop through and apply any pending layer changes - for (int i = 0; i < mLayerUpdates.size(); i++) { - HardwareLayer layer = mLayerUpdates.get(i); - layer.flushChanges(); - if (!layer.isValid()) { - // The layer was removed from mAttachedLayers, rewind i by 1 - // Note that this shouldn't actually happen as View.getHardwareLayer() - // is already flushing for error checking reasons - i--; - } else if (layer.hasDisplayList()) { - mCanvas.pushLayerUpdate(layer); - } - } - mLayerUpdates.clear(); - } - - @Override - void fence() { - // Everything is immediate, so this is a no-op - } - - private RenderNode buildDisplayList(View view, HardwareCanvas canvas) { - view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) - == View.PFLAG_INVALIDATED; - view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; - - long buildDisplayListStartTime = startBuildDisplayListProfiling(); - canvas.clearLayerUpdates(); - - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); - RenderNode renderNode = view.getDisplayList(); - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - - endBuildDisplayListProfiling(buildDisplayListStartTime); - - return renderNode; - } - - private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) { - // We had to change the current surface and/or context, redraw everything - if (surfaceState == SURFACE_STATE_UPDATED) { - dirty = null; - beginFrame(null); - } else { - int[] size = mSurfaceSize; - beginFrame(size); - - if (size[1] != mHeight || size[0] != mWidth) { - mWidth = size[0]; - mHeight = size[1]; - - canvas.setViewport(mWidth, mHeight); - - dirty = null; - } - } - - if (mDebugDataProvider != null) dirty = null; - - return dirty; - } - - private long startBuildDisplayListProfiling() { - if (mProfileEnabled) { - mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; - if (mProfileCurrentFrame >= mProfileData.length) { - mProfileCurrentFrame = 0; - } - - return System.nanoTime(); - } - return 0; - } - - private void endBuildDisplayListProfiling(long getDisplayListStartTime) { - if (mProfileEnabled) { - long now = System.nanoTime(); - float total = (now - getDisplayListStartTime) * 0.000001f; - //noinspection PointlessArithmeticExpression - mProfileData[mProfileCurrentFrame] = total; - } - } - - private int prepareFrame(Rect dirty) { - int status; - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame"); - try { - status = onPreDraw(dirty); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } - return status; - } - - private int drawDisplayList(HardwareCanvas canvas, RenderNode displayList, - int status) { - - long drawDisplayListStartTime = 0; - if (mProfileEnabled) { - drawDisplayListStartTime = System.nanoTime(); - } - - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList"); - nPrepareTree(displayList.getNativeDisplayList()); - try { - status |= canvas.drawDisplayList(displayList, mRedrawClip, - RenderNode.FLAG_CLIP_CHILDREN); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - } - - if (mProfileEnabled) { - long now = System.nanoTime(); - float total = (now - drawDisplayListStartTime) * 0.000001f; - mProfileData[mProfileCurrentFrame + 1] = total; - } - - return status; - } - - private void swapBuffers(int status) { - if ((status & RenderNode.STATUS_DREW) == RenderNode.STATUS_DREW) { - long eglSwapBuffersStartTime = 0; - if (mProfileEnabled) { - eglSwapBuffersStartTime = System.nanoTime(); - } - - sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); - - if (mProfileEnabled) { - long now = System.nanoTime(); - float total = (now - eglSwapBuffersStartTime) * 0.000001f; - mProfileData[mProfileCurrentFrame + 2] = total; - } - - checkEglErrors(); - } - } - - private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) { - if (mDebugDirtyRegions) { - if (mDebugPaint == null) { - mDebugPaint = new Paint(); - mDebugPaint.setColor(0x7fff0000); - } - - if (dirty != null && (mFrameCount & 1) == 0) { - canvas.drawRect(dirty, mDebugPaint); - } - } - } - - /** - * Ensures the current EGL context and surface are the ones we expect. - * This method throws an IllegalStateException if invoked from a thread - * that did not initialize EGL. - * - * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, - * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or - * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one - * - * @see #checkRenderContextUnsafe() - */ - int checkRenderContext() { - if (mEglThread != Thread.currentThread()) { - throw new IllegalStateException("Hardware acceleration can only be used with a " + - "single UI thread.\nOriginal thread: " + mEglThread + "\n" + - "Current thread: " + Thread.currentThread()); - } - - return checkRenderContextUnsafe(); - } - - /** - * Ensures the current EGL context and surface are the ones we expect. - * This method does not check the current thread. - * - * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current, - * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or - * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one - * - * @see #checkRenderContext() - */ - private int checkRenderContextUnsafe() { - if (!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW)) || - !mEglContext.equals(sEgl.eglGetCurrentContext())) { - if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) { - Log.e(LOG_TAG, "eglMakeCurrent failed " + - GLUtils.getEGLErrorString(sEgl.eglGetError())); - fallback(true); - return SURFACE_STATE_ERROR; - } else { - if (mUpdateDirtyRegions) { - enableDirtyRegions(); - mUpdateDirtyRegions = false; - } - return SURFACE_STATE_UPDATED; - } - } - return SURFACE_STATE_SUCCESS; - } - - private static int dpToPx(int dp, float density) { - return (int) (dp * density + 0.5f); - } - - static native boolean loadProperties(); - - static native void setupShadersDiskCache(String cacheFile); - - /** - * Notifies EGL that the frame is about to be rendered. - * @param size - */ - static native void beginFrame(int[] size); - - /** - * Returns the current system time according to the renderer. - * This method is used for debugging only and should not be used - * as a clock. - */ - static native long getSystemTime(); - - /** - * Preserves the back buffer of the current surface after a buffer swap. - * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current - * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL - * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT. - * - * @return True if the swap behavior was successfully changed, - * false otherwise. - */ - static native boolean preserveBackBuffer(); - - /** - * Indicates whether the current surface preserves its back buffer - * after a buffer swap. - * - * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED, - * false otherwise - */ - static native boolean isBackBufferPreserved(); - - static native void nDestroyLayer(long layerPtr); - - private static native void nPrepareTree(long displayListPtr); - - class GraphDataProvider { - /** - * Draws the graph as bars. Frame elements are stacked on top of - * each other. - */ - public static final int GRAPH_TYPE_BARS = 0; - /** - * Draws the graph as lines. The number of series drawn corresponds - * to the number of elements. - */ - public static final int GRAPH_TYPE_LINES = 1; - - private final int mGraphType; - - private int mVerticalUnit; - private int mHorizontalUnit; - private int mHorizontalMargin; - private int mThresholdStroke; - - public GraphDataProvider(int graphType) { - mGraphType = graphType; - } - - void prepare(DisplayMetrics metrics) { - final float density = metrics.density; - - mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density); - mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density); - mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density); - mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density); - } - - int getGraphType() { - return mGraphType; - } - - int getVerticalUnitSize() { - return mVerticalUnit; - } - - int getHorizontalUnitSize() { - return mHorizontalUnit; - } - - int getHorizontaUnitMargin() { - return mHorizontalMargin; - } - - float[] getData() { - return mProfileData; - } - - float getThreshold() { - return 16; - } - - int getFrameCount() { - return mProfileData.length / PROFILE_FRAME_DATA_COUNT; - } - - int getElementCount() { - return PROFILE_FRAME_DATA_COUNT; - } - - int getCurrentFrame() { - return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT; - } - - void setupGraphPaint(Paint paint, int elementIndex) { - paint.setColor(PROFILE_DRAW_COLORS[elementIndex]); - if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke); - } - - void setupThresholdPaint(Paint paint) { - paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR); - paint.setStrokeWidth(mThresholdStroke); - } - - void setupCurrentFramePaint(Paint paint) { - paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR); - if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke); - } - } -} diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index 9568760..b8e7d8c 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -110,48 +110,6 @@ public abstract class HardwareCanvas extends Canvas { return RenderNode.STATUS_DONE; } - /** - * Indicates that the specified layer must be updated as soon as possible. - * - * @param layer The layer to update - * - * @see #clearLayerUpdates() - * - * @hide - */ - abstract void pushLayerUpdate(HardwareLayer layer); - - /** - * Cancels a queued layer update. If the specified layer was not - * queued for update, this method has no effect. - * - * @param layer The layer whose update to cancel - * - * @see #pushLayerUpdate(HardwareLayer) - * @see #clearLayerUpdates() - * - * @hide - */ - abstract void cancelLayerUpdate(HardwareLayer layer); - - /** - * Immediately executes all enqueued layer updates. - * - * @see #pushLayerUpdate(HardwareLayer) - * - * @hide - */ - abstract void flushLayerUpdates(); - - /** - * Removes all enqueued layer updates. - * - * @see #pushLayerUpdate(HardwareLayer) - * - * @hide - */ - abstract void clearLayerUpdates(); - public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy, CanvasProperty<Float> radius, CanvasProperty<Paint> paint); } diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index 652bcd2..6acb134 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -172,24 +172,6 @@ final class HardwareLayer { }); } - /** - * This exists to minimize impact into the current HardwareLayer paths as - * some of the specifics of how to handle error cases in the fully - * deferred model will work - */ - @Deprecated - public void flushChanges() { - if (HardwareRenderer.sUseRenderThread) { - // Not supported, don't try. - return; - } - - boolean success = nFlushChanges(mFinalizer.get()); - if (!success) { - destroy(); - } - } - public long getLayer() { return nGetLayer(mFinalizer.get()); } @@ -216,33 +198,14 @@ final class HardwareLayer { return st; } - /** - * This should only be used by HardwareRenderer! Do not call directly - */ - static HardwareLayer createTextureLayer(HardwareRenderer renderer) { - return new HardwareLayer(renderer, nCreateTextureLayer(), LAYER_TYPE_TEXTURE); - } - static HardwareLayer adoptTextureLayer(HardwareRenderer renderer, long layer) { return new HardwareLayer(renderer, layer, LAYER_TYPE_TEXTURE); } - /** - * This should only be used by HardwareRenderer! Do not call directly - */ - static HardwareLayer createDisplayListLayer(HardwareRenderer renderer, - int width, int height) { - return new HardwareLayer(renderer, nCreateRenderLayer(width, height), LAYER_TYPE_DISPLAY_LIST); - } - static HardwareLayer adoptDisplayListLayer(HardwareRenderer renderer, long layer) { return new HardwareLayer(renderer, layer, LAYER_TYPE_DISPLAY_LIST); } - /** This also creates the underlying layer */ - private static native long nCreateTextureLayer(); - private static native long nCreateRenderLayer(int width, int height); - private static native void nOnTextureDestroyed(long layerUpdater); private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index d71de9f..d67c974 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -171,9 +171,6 @@ public abstract class HardwareRenderer { */ public static boolean sSystemRendererDisabled = false; - /** @hide */ - public static boolean sUseRenderThread = true; - private boolean mEnabled; private boolean mRequested = true; @@ -309,7 +306,7 @@ public abstract class HardwareRenderer { * @hide */ public static void setupDiskCache(File cacheDir) { - GLRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); + ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); } /** @@ -469,11 +466,7 @@ public abstract class HardwareRenderer { static HardwareRenderer create(boolean translucent) { HardwareRenderer renderer = null; if (GLES20Canvas.isAvailable()) { - if (sUseRenderThread) { - renderer = new ThreadedRenderer(translucent); - } else { - renderer = new GLRenderer(translucent); - } + renderer = new ThreadedRenderer(translucent); } return renderer; } @@ -500,7 +493,7 @@ public abstract class HardwareRenderer { * see {@link android.content.ComponentCallbacks} */ static void startTrimMemory(int level) { - GLRenderer.startTrimMemory(level); + ThreadedRenderer.startTrimMemory(level); } /** @@ -508,7 +501,7 @@ public abstract class HardwareRenderer { * cleanup special resources used by the memory trimming process. */ static void endTrimMemory() { - GLRenderer.endTrimMemory(); + ThreadedRenderer.endTrimMemory(); } /** diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index c15ce44..5cd3d62 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -38,11 +38,11 @@ public class SurfaceControl { private static native void nativeDestroy(long nativeObject); private static native Bitmap nativeScreenshot(IBinder displayToken, - int width, int height, int minLayer, int maxLayer, boolean allLayers, - boolean useIdentityTransform); + Rect sourceCrop, int width, int height, int minLayer, int maxLayer, + boolean allLayers, boolean useIdentityTransform); private static native void nativeScreenshot(IBinder displayToken, Surface consumer, - int width, int height, int minLayer, int maxLayer, boolean allLayers, - boolean useIdentityTransform); + Rect sourceCrop, int width, int height, int minLayer, int maxLayer, + boolean allLayers, boolean useIdentityTransform); private static native void nativeOpenTransaction(); private static native void nativeCloseTransaction(); @@ -597,8 +597,8 @@ public class SurfaceControl { public static void screenshot(IBinder display, Surface consumer, int width, int height, int minLayer, int maxLayer, boolean useIdentityTransform) { - screenshot(display, consumer, width, height, minLayer, maxLayer, false, - useIdentityTransform); + screenshot(display, consumer, new Rect(), width, height, minLayer, maxLayer, + false, useIdentityTransform); } /** @@ -613,7 +613,7 @@ public class SurfaceControl { */ public static void screenshot(IBinder display, Surface consumer, int width, int height) { - screenshot(display, consumer, width, height, 0, 0, true, false); + screenshot(display, consumer, new Rect(), width, height, 0, 0, true, false); } /** @@ -623,7 +623,7 @@ public class SurfaceControl { * @param consumer The {@link Surface} to take the screenshot into. */ public static void screenshot(IBinder display, Surface consumer) { - screenshot(display, consumer, 0, 0, 0, 0, true, false); + screenshot(display, consumer, new Rect(), 0, 0, 0, 0, true, false); } /** @@ -634,6 +634,8 @@ public class SurfaceControl { * the versions that use a {@link Surface} instead, such as * {@link SurfaceControl#screenshot(IBinder, Surface)}. * + * @param sourceCrop The portion of the screen to capture into the Bitmap; + * caller may pass in 'new Rect()' if no cropping is desired. * @param width The desired width of the returned bitmap; the raw * screen will be scaled down to this size. * @param height The desired height of the returned bitmap; the raw @@ -649,13 +651,13 @@ public class SurfaceControl { * if an error occurs. Make sure to call Bitmap.recycle() as soon as * possible, once its content is not needed anymore. */ - public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer, - boolean useIdentityTransform) { + public static Bitmap screenshot(Rect sourceCrop, int width, int height, + int minLayer, int maxLayer, boolean useIdentityTransform) { // TODO: should take the display as a parameter IBinder displayToken = SurfaceControl.getBuiltInDisplay( SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false, - useIdentityTransform); + return nativeScreenshot(displayToken, sourceCrop, width, height, + minLayer, maxLayer, false, useIdentityTransform); } /** @@ -674,10 +676,10 @@ public class SurfaceControl { // TODO: should take the display as a parameter IBinder displayToken = SurfaceControl.getBuiltInDisplay( SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); - return nativeScreenshot(displayToken, width, height, 0, 0, true, false); + return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true, false); } - private static void screenshot(IBinder display, Surface consumer, + private static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, int width, int height, int minLayer, int maxLayer, boolean allLayers, boolean useIdentityTransform) { if (display == null) { @@ -686,7 +688,7 @@ public class SurfaceControl { if (consumer == null) { throw new IllegalArgumentException("consumer must not be null"); } - nativeScreenshot(display, consumer, width, height, minLayer, maxLayer, allLayers, - useIdentityTransform); + nativeScreenshot(display, consumer, sourceCrop, width, height, + minLayer, maxLayer, allLayers, useIdentityTransform); } } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 11db996..9b3ef7f 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -331,6 +331,14 @@ public class ThreadedRenderer extends HardwareRenderer { } } + static void startTrimMemory(int level) { + // TODO + } + + static void endTrimMemory() { + // TODO + } + private static class AtlasInitializer { static AtlasInitializer sInstance = new AtlasInitializer(); @@ -367,6 +375,8 @@ public class ThreadedRenderer extends HardwareRenderer { } } + static native void setupShadersDiskCache(String cacheFile); + private static native void nSetAtlas(GraphicBuffer buffer, long[] map); private static native long nCreateRootRenderNode(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c7c007e..622fa8c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9236,6 +9236,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Request unbuffered dispatch of the given stream of MotionEvents to this View. + * + * Until this View receives a corresponding {@link MotionEvent#ACTION_UP}, ask that the input + * system not batch {@link MotionEvent}s but instead deliver them as soon as they're + * available. This method should only be called for touch events. + * + * <p class="note">This api is not intended for most applications. Buffered dispatch + * provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent + * streams will not improve your input latency. Side effects include: increased latency, + * jittery scrolls and inability to take advantage of system resampling. Talk to your input + * professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for + * you.</p> + */ + public final void requestUnbufferedDispatch(MotionEvent event) { + final int action = event.getAction(); + if (mAttachInfo == null + || action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_MOVE + || !event.isTouchEvent()) { + return; + } + mAttachInfo.mUnbufferedDispatchRequested = true; + } + + /** * Set flags controlling behavior of this view. * * @param flags Constant indicating the value which should be set @@ -13569,12 +13593,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - // The layer is not valid if the underlying GPU resources cannot be allocated - mHardwareLayer.flushChanges(); - if (!mHardwareLayer.isValid()) { - return null; - } - mHardwareLayer.setLayerPaint(mLayerPaint); RenderNode displayList = mHardwareLayer.startRecording(); updateDisplayListIfDirty(displayList, true); @@ -19774,6 +19792,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mInTouchMode; /** + * Indicates whether the view has requested unbuffered input dispatching for the current + * event stream. + */ + boolean mUnbufferedDispatchRequested; + + /** * Indicates that ViewAncestor should trigger a global layout change * the next time it performs a traversal */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index aa06d15..f3d1e3c 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -230,6 +230,7 @@ public final class ViewRootImpl implements ViewParent, QueuedInputEvent mPendingInputEventTail; int mPendingInputEventCount; boolean mProcessInputEventsScheduled; + boolean mUnbufferedInputDispatch; String mPendingInputEventQueueLengthCounterName = "pq"; InputStage mFirstInputStage; @@ -715,17 +716,6 @@ public final class ViewRootImpl implements ViewParent, if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) { - if (!HardwareRenderer.sUseRenderThread) { - // TODO: Delete - // Don't enable hardware acceleration when we're not on the main thread - if (!HardwareRenderer.sSystemRendererDisabled && - Looper.getMainLooper() != Looper.myLooper()) { - Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " - + "acceleration outside of the main thread, aborting"); - return; - } - } - if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.destroy(true); } @@ -1016,7 +1006,9 @@ public final class ViewRootImpl implements ViewParent, mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); - scheduleConsumeBatchedInput(); + if (!mUnbufferedInputDispatch) { + scheduleConsumeBatchedInput(); + } notifyRendererOfFramePending(); } } @@ -2616,7 +2608,7 @@ public final class ViewRootImpl implements ViewParent, } final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider(); - final Rect bounds = mView.mAttachInfo.mTmpInvalRect; + final Rect bounds = mAttachInfo.mTmpInvalRect; if (provider == null) { host.getBoundsOnScreen(bounds); } else if (mAccessibilityFocusedVirtualView != null) { @@ -3898,6 +3890,18 @@ public final class ViewRootImpl implements ViewParent, } } + @Override + protected void onDeliverToNext(QueuedInputEvent q) { + if (mUnbufferedInputDispatch + && q.mEvent instanceof MotionEvent + && ((MotionEvent)q.mEvent).isTouchEvent() + && isTerminalInputEvent(q.mEvent)) { + mUnbufferedInputDispatch = false; + scheduleConsumeBatchedInput(); + } + super.onDeliverToNext(q); + } + private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; @@ -4010,10 +4014,15 @@ public final class ViewRootImpl implements ViewParent, private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; - if (mView.dispatchPointerEvent(event)) { - return FINISH_HANDLED; + mAttachInfo.mUnbufferedDispatchRequested = false; + boolean handled = mView.dispatchPointerEvent(event); + if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) { + mUnbufferedInputDispatch = true; + if (mConsumeBatchedInputScheduled) { + scheduleConsumeBatchedInputImmediately(); + } } - return FORWARD; + return handled ? FINISH_HANDLED : FORWARD; } private int processTrackballEvent(QueuedInputEvent q) { @@ -5278,6 +5287,8 @@ public final class ViewRootImpl implements ViewParent, writer.print(" mRemoved="); writer.println(mRemoved); writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled="); writer.println(mConsumeBatchedInputScheduled); + writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled="); + writer.println(mConsumeBatchedInputImmediatelyScheduled); writer.print(innerPrefix); writer.print("mPendingInputEventCount="); writer.println(mPendingInputEventCount); writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled="); @@ -5688,6 +5699,7 @@ public final class ViewRootImpl implements ViewParent, private void finishInputEvent(QueuedInputEvent q) { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent", q.mEvent.getSequenceNumber()); + if (q.mReceiver != null) { boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0; q.mReceiver.finishInputEvent(q.mEvent, handled); @@ -5727,15 +5739,25 @@ public final class ViewRootImpl implements ViewParent, } } + void scheduleConsumeBatchedInputImmediately() { + if (!mConsumeBatchedInputImmediatelyScheduled) { + unscheduleConsumeBatchedInput(); + mConsumeBatchedInputImmediatelyScheduled = true; + mHandler.post(mConsumeBatchedInputImmediatelyRunnable); + } + } + void doConsumeBatchedInput(long frameTimeNanos) { if (mConsumeBatchedInputScheduled) { mConsumeBatchedInputScheduled = false; if (mInputEventReceiver != null) { - if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)) { + if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos) + && frameTimeNanos != -1) { // If we consumed a batch here, we want to go ahead and schedule the // consumption of batched input events on the next frame. Otherwise, we would // wait until we have more input events pending and might get starved by other - // things occurring in the process. + // things occurring in the process. If the frame time is -1, however, then + // we're in a non-batching mode, so there's no need to schedule this. scheduleConsumeBatchedInput(); } } @@ -5763,7 +5785,11 @@ public final class ViewRootImpl implements ViewParent, @Override public void onBatchedInputEventPending() { - scheduleConsumeBatchedInput(); + if (mUnbufferedInputDispatch) { + super.onBatchedInputEventPending(); + } else { + scheduleConsumeBatchedInput(); + } } @Override @@ -5784,6 +5810,16 @@ public final class ViewRootImpl implements ViewParent, new ConsumeBatchedInputRunnable(); boolean mConsumeBatchedInputScheduled; + final class ConsumeBatchedInputImmediatelyRunnable implements Runnable { + @Override + public void run() { + doConsumeBatchedInput(-1); + } + } + final ConsumeBatchedInputImmediatelyRunnable mConsumeBatchedInputImmediatelyRunnable = + new ConsumeBatchedInputImmediatelyRunnable(); + boolean mConsumeBatchedInputImmediatelyScheduled; + final class InvalidateOnAnimationRunnable implements Runnable { private boolean mPosted; private final ArrayList<View> mViews = new ArrayList<View>(); diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 628da3c..84f395a 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -427,8 +427,12 @@ public class SpellCheckerSession { @Override public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) { - mHandler.sendMessage( - Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results)); + synchronized (this) { + if (mHandler != null) { + mHandler.sendMessage(Message.obtain(mHandler, + MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results)); + } + } } } diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 265dbcd..2c1a77c 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -24,6 +24,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.text.InputType; +import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.AttributeSet; import android.util.Log; @@ -814,8 +815,7 @@ public class DatePicker extends FrameLayout { mSpinners.removeAllViews(); // We use numeric spinners for year and day, but textual months. Ask icu4c what // order the user's locale uses for that combination. http://b/7207103. - String pattern = ICU.getBestDateTimePattern("yyyyMMMdd", - Locale.getDefault().toString()); + String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd"); char[] order = ICU.getDateFormatOrder(pattern); final int spinnerCount = order.length; for (int i = 0; i < spinnerCount; i++) { diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 8511601..defc26c 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -104,14 +104,16 @@ import static java.lang.Math.min; * * <h4>Excess Space Distribution</h4> * - * GridLayout's distribution of excess space is based on <em>priority</em> - * rather than <em>weight</em>. + * As of API 21, GridLayout's distribution of excess space accomodates the principle of weight. + * In the event that no weights are specified, the previous conventions are respected and + * columns and rows are taken as flexible if their views specify some form of alignment + * within their groups. * <p> - * A child's ability to stretch is inferred from the alignment properties of - * its row and column groups (which are typically set by setting the - * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters). - * If alignment was defined along a given axis then the component - * is taken as <em>flexible</em> in that direction. If no alignment was set, + * The flexibility of a view is therefore influenced by its alignment which is, + * in turn, typically defined by setting the + * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters. + * If either a weight or alignment were defined along a given axis then the component + * is taken as <em>flexible</em> in that direction. If no weight or alignment was set, * the component is instead assumed to be <em>inflexible</em>. * <p> * Multiple components in the same row or column group are @@ -122,12 +124,16 @@ import static java.lang.Math.min; * elements is flexible if <em>one</em> of its elements is flexible. * <p> * To make a column stretch, make sure all of the components inside it define a - * gravity. To prevent a column from stretching, ensure that one of the components - * in the column does not define a gravity. + * weight or a gravity. To prevent a column from stretching, ensure that one of the components + * in the column does not define a weight or a gravity. * <p> * When the principle of flexibility does not provide complete disambiguation, * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em> - * and <em>bottom</em> edges. + * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout + * parameters as a constraint in the a set of variables that define the grid-lines along a + * given axis. During layout, GridLayout solves the constraints so as to return the unique + * solution to those constraints for which all variables are less-than-or-equal-to + * the corresponding value in any other valid solution. * * <h4>Interpretation of GONE</h4> * @@ -140,18 +146,6 @@ import static java.lang.Math.min; * had never been added to it. * These statements apply equally to rows as well as columns, and to groups of rows or columns. * - * <h5>Limitations</h5> - * - * GridLayout does not provide support for the principle of <em>weight</em>, as defined in - * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible - * to configure a GridLayout to distribute excess space between multiple components. - * <p> - * Some common use-cases may nevertheless be accommodated as follows. - * To place equal amounts of space around a component in a cell group; - * use {@link #CENTER} alignment (or {@link LayoutParams#setGravity(int) gravity}). - * For complete control over excess space distribution in a row or column; - * use a {@link LinearLayout} subview to hold the components in the associated cell group. - * When using either of these techniques, bear in mind that cell groups may be defined to overlap. * <p> * See {@link GridLayout.LayoutParams} for a full description of the * layout parameters used by GridLayout. @@ -1018,6 +1012,8 @@ public class GridLayout extends ViewGroup { LayoutParams lp = getLayoutParams(c); if (firstPass) { measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height); + mHorizontalAxis.recordOriginalMeasurement(i); + mVerticalAxis.recordOriginalMeasurement(i); } else { boolean horizontal = (mOrientation == HORIZONTAL); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; @@ -1245,6 +1241,11 @@ public class GridLayout extends ViewGroup { public int[] locations; public boolean locationsValid = false; + public boolean hasWeights; + public boolean hasWeightsValid = false; + public int[] originalMeasurements; + public int[] deltas; + boolean orderPreserved = DEFAULT_ORDER_PRESERVED; private MutableInt parentMin = new MutableInt(0); @@ -1321,7 +1322,10 @@ public class GridLayout extends ViewGroup { // we must include views that are GONE here, see introductory javadoc LayoutParams lp = getLayoutParams(c); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; - groupBounds.getValue(i).include(GridLayout.this, c, spec, this); + int size = (spec.weight == 0) ? + getMeasurementIncludingMargin(c, horizontal) : + getOriginalMeasurements()[i] + getDeltas()[i]; + groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size); } } @@ -1693,8 +1697,94 @@ public class GridLayout extends ViewGroup { return trailingMargins; } - private void computeLocations(int[] a) { + private void solve(int[] a) { solve(getArcs(), a); + } + + private boolean computeHasWeights() { + for (int i = 0, N = getChildCount(); i < N; i++) { + LayoutParams lp = getLayoutParams(getChildAt(i)); + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + if (spec.weight != 0) { + return true; + } + } + return false; + } + + private boolean hasWeights() { + if (!hasWeightsValid) { + hasWeights = computeHasWeights(); + hasWeightsValid = true; + } + return hasWeights; + } + + public int[] getOriginalMeasurements() { + if (originalMeasurements == null) { + originalMeasurements = new int[getChildCount()]; + } + return originalMeasurements; + } + + private void recordOriginalMeasurement(int i) { + if (hasWeights()) { + getOriginalMeasurements()[i] = getMeasurementIncludingMargin(getChildAt(i), horizontal); + } + } + + public int[] getDeltas() { + if (deltas == null) { + deltas = new int[getChildCount()]; + } + return deltas; + } + + private void shareOutDelta() { + int totalDelta = 0; + float totalWeight = 0; + for (int i = 0, N = getChildCount(); i < N; i++) { + View c = getChildAt(i); + LayoutParams lp = getLayoutParams(c); + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + float weight = spec.weight; + if (weight != 0) { + int delta = getMeasurement(c, horizontal) - getOriginalMeasurements()[i]; + totalDelta += delta; + totalWeight += weight; + } + } + for (int i = 0, N = getChildCount(); i < N; i++) { + LayoutParams lp = getLayoutParams(getChildAt(i)); + Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; + float weight = spec.weight; + if (weight != 0) { + int delta = Math.round((weight * totalDelta / totalWeight)); + deltas[i] = delta; + // the two adjustments below are to counter the above rounding and avoid off-by-ones at the end + totalDelta -= delta; + totalWeight -= weight; + } + } + } + + private void solveAndDistributeSpace(int[] a) { + Arrays.fill(getDeltas(), 0); + solve(a); + shareOutDelta(); + arcsValid = false; + forwardLinksValid = false; + backwardLinksValid = false; + groupBoundsValid = false; + solve(a); + } + + private void computeLocations(int[] a) { + if (!hasWeights()) { + solve(a); + } else { + solveAndDistributeSpace(a); + } if (!orderPreserved) { // Solve returns the smallest solution to the constraint system for which all // values are positive. One value is therefore zero - though if the row/col @@ -1777,6 +1867,10 @@ public class GridLayout extends ViewGroup { locations = null; + originalMeasurements = null; + deltas = null; + hasWeightsValid = false; + invalidateValues(); } @@ -1810,6 +1904,9 @@ public class GridLayout extends ViewGroup { * both aspects of alignment within the cell group. It is also possible to specify a child's * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)} * method. + * <p> + * The weight property is also included in Spec and specifies the proportion of any + * excess space that is due to the associated view. * * <h4>WRAP_CONTENT and MATCH_PARENT</h4> * @@ -1851,9 +1948,11 @@ public class GridLayout extends ViewGroup { * <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li> * <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li> * <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li> + * <li>{@link #rowSpec}<code>.weight</code> = 0 </li> * <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li> * <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li> * <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li> + * <li>{@link #columnSpec}<code>.weight</code> = 0 </li> * </ul> * * See {@link GridLayout} for a more complete description of the conventions @@ -1861,8 +1960,10 @@ public class GridLayout extends ViewGroup { * * @attr ref android.R.styleable#GridLayout_Layout_layout_row * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan + * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight * @attr ref android.R.styleable#GridLayout_Layout_layout_column * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan + * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity */ public static class LayoutParams extends MarginLayoutParams { @@ -1889,9 +1990,11 @@ public class GridLayout extends ViewGroup { private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column; private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan; + private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight; private static final int ROW = R.styleable.GridLayout_Layout_layout_row; private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan; + private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight; private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity; @@ -2034,11 +2137,13 @@ public class GridLayout extends ViewGroup { int column = a.getInt(COLUMN, DEFAULT_COLUMN); int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE); - this.columnSpec = spec(column, colSpan, getAlignment(gravity, true)); + float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT); + this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight); int row = a.getInt(ROW, DEFAULT_ROW); int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE); - this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false)); + float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT); + this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight); } finally { a.recycle(); } @@ -2273,10 +2378,9 @@ public class GridLayout extends ViewGroup { return before - a.getAlignmentValue(c, size, gl.getLayoutMode()); } - protected final void include(GridLayout gl, View c, Spec spec, Axis axis) { + protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) { this.flexibility &= spec.getFlexibility(); boolean horizontal = axis.horizontal; - int size = gl.getMeasurementIncludingMargin(c, horizontal); Alignment alignment = gl.getAlignment(spec.alignment, horizontal); // todo test this works correctly when the returned value is UNDEFINED int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode()); @@ -2401,36 +2505,43 @@ public class GridLayout extends ViewGroup { * <li>{@link #spec(int, int)}</li> * <li>{@link #spec(int, Alignment)}</li> * <li>{@link #spec(int, int, Alignment)}</li> + * <li>{@link #spec(int, float)}</li> + * <li>{@link #spec(int, int, float)}</li> + * <li>{@link #spec(int, Alignment, float)}</li> + * <li>{@link #spec(int, int, Alignment, float)}</li> * </ul> * */ public static class Spec { static final Spec UNDEFINED = spec(GridLayout.UNDEFINED); + static final float DEFAULT_WEIGHT = 0; final boolean startDefined; final Interval span; final Alignment alignment; + final float weight; - private Spec(boolean startDefined, Interval span, Alignment alignment) { + private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) { this.startDefined = startDefined; this.span = span; this.alignment = alignment; + this.weight = weight; } - private Spec(boolean startDefined, int start, int size, Alignment alignment) { - this(startDefined, new Interval(start, start + size), alignment); + private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) { + this(startDefined, new Interval(start, start + size), alignment, weight); } final Spec copyWriteSpan(Interval span) { - return new Spec(startDefined, span, alignment); + return new Spec(startDefined, span, alignment, weight); } final Spec copyWriteAlignment(Alignment alignment) { - return new Spec(startDefined, span, alignment); + return new Spec(startDefined, span, alignment, weight); } final int getFlexibility() { - return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH; + return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH; } /** @@ -2478,6 +2589,7 @@ public class GridLayout extends ViewGroup { * <ul> * <li> {@code spec.span = [start, start + size]} </li> * <li> {@code spec.alignment = alignment} </li> + * <li> {@code spec.weight = weight} </li> * </ul> * <p> * To leave the start index undefined, use the value {@link #UNDEFINED}. @@ -2485,9 +2597,55 @@ public class GridLayout extends ViewGroup { * @param start the start * @param size the size * @param alignment the alignment + * @param weight the weight + */ + public static Spec spec(int start, int size, Alignment alignment, float weight) { + return new Spec(start != UNDEFINED, start, size, alignment, weight); + } + + /** + * Equivalent to: {@code spec(start, 1, alignment, weight)}. + * + * @param start the start + * @param alignment the alignment + * @param weight the weight + */ + public static Spec spec(int start, Alignment alignment, float weight) { + return spec(start, 1, alignment, weight); + } + + /** + * Equivalent to: {@code spec(start, 1, default_alignment, weight)} - + * where {@code default_alignment} is specified in + * {@link android.widget.GridLayout.LayoutParams}. + * + * @param start the start + * @param size the size + * @param weight the weight + */ + public static Spec spec(int start, int size, float weight) { + return spec(start, size, UNDEFINED_ALIGNMENT, weight); + } + + /** + * Equivalent to: {@code spec(start, 1, weight)}. + * + * @param start the start + * @param weight the weight + */ + public static Spec spec(int start, float weight) { + return spec(start, 1, weight); + } + + /** + * Equivalent to: {@code spec(start, size, alignment, 0f)}. + * + * @param start the start + * @param size the size + * @param alignment the alignment */ public static Spec spec(int start, int size, Alignment alignment) { - return new Spec(start != UNDEFINED, start, size, alignment); + return spec(start, size, alignment, Spec.DEFAULT_WEIGHT); } /** diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index 03d3b22..77f0dec 100644 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -25,16 +25,18 @@ import android.content.res.ObbInfo; interface IMediaContainerService { String copyResourceToContainer(in Uri packageURI, String containerId, String key, String resFileName, String publicResFileName, boolean isExternal, - boolean isForwardLocked); + boolean isForwardLocked, in String abiOverride); int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams, in ParcelFileDescriptor outStream); - PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold); + PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold, + in String abiOverride); boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold); - boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked); + boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in String abiOverride); ObbInfo getObbInfo(in String filename); long calculateDirectorySize(in String directory); /** Return file system stats: [0] is total bytes, [1] is available bytes */ long[] getFileSystemStats(in String path); void clearDirectory(in String directory); - long calculateInstalledSize(in String packagePath, boolean isForwardLocked); + long calculateInstalledSize(in String packagePath, boolean isForwardLocked, + in String abiOverride); } diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 47ef65a..01e5d40 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -35,8 +35,8 @@ import java.util.Set; /* - * This is used in conjunction with DevicePolicyManager.setForwardingIntents to enable intents to be - * passed in and out of a managed profile. + * This is used in conjunction with the {@link setCrossProfileIntentFilter} method of + * {@link DevicePolicyManager} to enable intents to be passed in and out of a managed profile. */ public class IntentForwarderActivity extends Activity { diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 591267e..183dd05 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -484,8 +484,7 @@ public class ResolverActivity extends AlertActivity implements AdapterView.OnIte mList.clear(); if (mBaseResolveList != null) { - currentResolveList = mBaseResolveList; - mOrigResolveList = null; + currentResolveList = mOrigResolveList = mBaseResolveList; } else { currentResolveList = mOrigResolveList = mPm.queryIntentActivities( mIntent, PackageManager.MATCH_DEFAULT_ONLY diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 1e37fd9..d10451b 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -178,7 +178,7 @@ interface IBackupTransport { /** * Get the data for the application returned by {@link #nextRestorePackage}. * @param data An open, writable file into which the backup data should be stored. - * @return the same error codes as {@link #nextRestorePackage}. + * @return the same error codes as {@link #startRestore}. */ int getRestoreData(in ParcelFileDescriptor outFd); diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 446ef55..7292116 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -18,6 +18,7 @@ package com.android.internal.backup; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.app.backup.BackupTransport; import android.app.backup.RestoreSet; import android.content.ComponentName; import android.content.Context; @@ -47,7 +48,7 @@ import static android.system.OsConstants.*; * later restoring from there. For testing only. */ -public class LocalTransport extends IBackupTransport.Stub { +public class LocalTransport extends BackupTransport { private static final String TAG = "LocalTransport"; private static final boolean DEBUG = true; @@ -103,7 +104,7 @@ public class LocalTransport extends IBackupTransport.Stub { public int initializeDevice() { if (DEBUG) Log.v(TAG, "wiping all data"); deleteContents(mCurrentSetDir); - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) { @@ -165,7 +166,7 @@ public class LocalTransport extends IBackupTransport.Stub { entity.write(buf, 0, dataSize); } catch (IOException e) { Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } finally { entity.close(); } @@ -173,11 +174,11 @@ public class LocalTransport extends IBackupTransport.Stub { entityFile.delete(); } } - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } catch (IOException e) { // oops, something went wrong. abort the operation and return error. Log.v(TAG, "Exception reading backup input:", e); - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } } @@ -207,17 +208,17 @@ public class LocalTransport extends IBackupTransport.Stub { } packageDir.delete(); } - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } public int finishBackup() { if (DEBUG) Log.v(TAG, "finishBackup()"); - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } // Restore handling static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 }; - public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { + public RestoreSet[] getAvailableRestoreSets() { long[] existing = new long[POSSIBLE_SETS.length + 1]; int num = 0; @@ -248,7 +249,7 @@ public class LocalTransport extends IBackupTransport.Stub { mRestorePackage = -1; mRestoreToken = token; mRestoreDataDir = new File(mDataDir, Long.toString(token)); - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } public String nextRestorePackage() { @@ -280,7 +281,7 @@ public class LocalTransport extends IBackupTransport.Stub { ArrayList<DecodedFilename> blobs = contentsByKey(packageDir); if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error Log.e(TAG, "No keys for package: " + packageDir); - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } // We expect at least some data if the directory exists in the first place @@ -301,10 +302,10 @@ public class LocalTransport extends IBackupTransport.Stub { in.close(); } } - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } catch (IOException e) { Log.e(TAG, "Unable to read backup records", e); - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } } diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/core/java/com/android/internal/backup/LocalTransportService.java index d05699a..77ac313 100644 --- a/core/java/com/android/internal/backup/LocalTransportService.java +++ b/core/java/com/android/internal/backup/LocalTransportService.java @@ -32,6 +32,6 @@ public class LocalTransportService extends Service { @Override public IBinder onBind(Intent intent) { - return sTransport; + return sTransport.getBinder(); } } diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index ba419f9..dab3aff 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -20,6 +20,7 @@ import android.content.pm.PackageManager; import android.util.Slog; import java.io.File; +import java.io.IOException; /** * Native libraries helper. @@ -141,4 +142,18 @@ public class NativeLibraryHelper { return deletedFiles; } + + // We don't care about the other return values for now. + private static final int BITCODE_PRESENT = 1; + + public static boolean hasRenderscriptBitcode(ApkHandle handle) throws IOException { + final int returnVal = hasRenderscriptBitcode(handle.apkHandle); + if (returnVal < 0) { + throw new IOException("Error scanning APK, code: " + returnVal); + } + + return (returnVal == BITCODE_PRESENT); + } + + private static native int hasRenderscriptBitcode(long apkHandle); } diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java index 495d5c6..7dbde69 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java @@ -19,6 +19,7 @@ package com.android.internal.inputmethod; import android.content.Context; import android.content.pm.PackageManager; import android.text.TextUtils; +import android.util.Log; import android.util.Slog; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; @@ -33,6 +34,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.TreeMap; /** @@ -116,6 +118,24 @@ public class InputMethodSubtypeSwitchingController { + " mIsSystemLanguage=" + mIsSystemLanguage + "}"; } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof ImeSubtypeListItem) { + final ImeSubtypeListItem that = (ImeSubtypeListItem)o; + if (!Objects.equals(this.mImi, that.mImi)) { + return false; + } + if (this.mSubtypeId != that.mSubtypeId) { + return false; + } + return true; + } + return false; + } } private static class InputMethodAndSubtypeList { @@ -211,54 +231,233 @@ public class InputMethodSubtypeSwitchingController { } } - private final InputMethodSettings mSettings; - private InputMethodAndSubtypeList mSubtypeList; + private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) { + return subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi, + subtype.hashCode()) : NOT_A_SUBTYPE_ID; + } - @VisibleForTesting - public static ImeSubtypeListItem getNextInputMethodLockedImpl(List<ImeSubtypeListItem> imList, - boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { - if (imi == null) { - return null; + private static class StaticRotationList { + private final List<ImeSubtypeListItem> mImeSubtypeList; + public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) { + mImeSubtypeList = imeSubtypeList; } - if (imList.size() <= 1) { - return null; - } - // Here we have two rotation groups, depending on the returned boolean value of - // {@link InputMethodInfo#supportsSwitchingToNextInputMethod()}. - final boolean expectedValueOfSupportsSwitchingToNextInputMethod = - imi.supportsSwitchingToNextInputMethod(); - final int N = imList.size(); - final int currentSubtypeId = - subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi, - subtype.hashCode()) : NOT_A_SUBTYPE_ID; - for (int i = 0; i < N; ++i) { - final ImeSubtypeListItem isli = imList.get(i); - // Skip until the current IME/subtype is found. - if (!isli.mImi.equals(imi) || isli.mSubtypeId != currentSubtypeId) { - continue; - } - // Found the current IME/subtype. Start searching the next IME/subtype from here. - for (int j = 0; j < N - 1; ++j) { - final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N); - // Skip if the candidate doesn't belong to the expected rotation group. - if (expectedValueOfSupportsSwitchingToNextInputMethod != - candidate.mImi.supportsSwitchingToNextInputMethod()) { - continue; + + /** + * Returns the index of the specified input method and subtype in the given list. + * @param imi The {@link InputMethodInfo} to be searched. + * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method + * does not have a subtype. + * @return The index in the given list. -1 if not found. + */ + private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) { + final int currentSubtypeId = calculateSubtypeId(imi, subtype); + final int N = mImeSubtypeList.size(); + for (int i = 0; i < N; ++i) { + final ImeSubtypeListItem isli = mImeSubtypeList.get(i); + // Skip until the current IME/subtype is found. + if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) { + return i; } + } + return -1; + } + + public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, + InputMethodInfo imi, InputMethodSubtype subtype) { + if (imi == null) { + return null; + } + if (mImeSubtypeList.size() <= 1) { + return null; + } + final int currentIndex = getIndex(imi, subtype); + if (currentIndex < 0) { + return null; + } + final int N = mImeSubtypeList.size(); + for (int offset = 1; offset < N; ++offset) { + // Start searching the next IME/subtype from the next of the current index. + final int candidateIndex = (currentIndex + offset) % N; + final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex); // Skip if searching inside the current IME only, but the candidate is not // the current IME. - if (onlyCurrentIme && !candidate.mImi.equals(imi)) { + if (onlyCurrentIme && !imi.equals(candidate.mImi)) { continue; } return candidate; } - // No appropriate IME/subtype is found in the list. Give up. return null; } - // The current IME/subtype is not found in the list. Give up. - return null; } + private static class DynamicRotationList { + private static final String TAG = DynamicRotationList.class.getSimpleName(); + private final List<ImeSubtypeListItem> mImeSubtypeList; + private final int[] mUsageHistoryOfSubtypeListItemIndex; + + private DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) { + mImeSubtypeList = imeSubtypeListItems; + mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()]; + final int N = mImeSubtypeList.size(); + for (int i = 0; i < N; i++) { + mUsageHistoryOfSubtypeListItemIndex[i] = i; + } + } + + /** + * Returns the index of the specified object in + * {@link #mUsageHistoryOfSubtypeListItemIndex}. + * <p>We call the index of {@link #mUsageHistoryOfSubtypeListItemIndex} as "Usage Rank" + * so as not to be confused with the index in {@link #mImeSubtypeList}. + * @return -1 when the specified item doesn't belong to {@link #mImeSubtypeList} actually. + */ + private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) { + final int currentSubtypeId = calculateSubtypeId(imi, subtype); + final int N = mUsageHistoryOfSubtypeListItemIndex.length; + for (int usageRank = 0; usageRank < N; usageRank++) { + final int subtypeListItemIndex = mUsageHistoryOfSubtypeListItemIndex[usageRank]; + final ImeSubtypeListItem subtypeListItem = + mImeSubtypeList.get(subtypeListItemIndex); + if (subtypeListItem.mImi.equals(imi) && + subtypeListItem.mSubtypeId == currentSubtypeId) { + return usageRank; + } + } + // Not found in the known IME/Subtype list. + return -1; + } + + public void onUserAction(InputMethodInfo imi, InputMethodSubtype subtype) { + final int currentUsageRank = getUsageRank(imi, subtype); + // Do nothing if currentUsageRank == -1 (not found), or currentUsageRank == 0 + if (currentUsageRank <= 0) { + return; + } + final int currentItemIndex = mUsageHistoryOfSubtypeListItemIndex[currentUsageRank]; + System.arraycopy(mUsageHistoryOfSubtypeListItemIndex, 0, + mUsageHistoryOfSubtypeListItemIndex, 1, currentUsageRank); + mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex; + } + + public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, + InputMethodInfo imi, InputMethodSubtype subtype) { + int currentUsageRank = getUsageRank(imi, subtype); + if (currentUsageRank < 0) { + if (DEBUG) { + Slog.d(TAG, "IME/subtype is not found: " + imi.getId() + ", " + subtype); + } + return null; + } + final int N = mUsageHistoryOfSubtypeListItemIndex.length; + for (int i = 1; i < N; i++) { + final int subtypeListItemRank = (currentUsageRank + i) % N; + final int subtypeListItemIndex = + mUsageHistoryOfSubtypeListItemIndex[subtypeListItemRank]; + final ImeSubtypeListItem subtypeListItem = + mImeSubtypeList.get(subtypeListItemIndex); + if (onlyCurrentIme && !imi.equals(subtypeListItem.mImi)) { + continue; + } + return subtypeListItem; + } + return null; + } + } + + @VisibleForTesting + public static class ControllerImpl { + private final DynamicRotationList mSwitchingAwareRotationList; + private final StaticRotationList mSwitchingUnawareRotationList; + + public static ControllerImpl createFrom(final ControllerImpl currentInstance, + final List<ImeSubtypeListItem> sortedEnabledItems) { + DynamicRotationList switchingAwareRotationList = null; + { + final List<ImeSubtypeListItem> switchingAwareImeSubtypes = + filterImeSubtypeList(sortedEnabledItems, + true /* supportsSwitchingToNextInputMethod */); + if (currentInstance != null && + currentInstance.mSwitchingAwareRotationList != null && + Objects.equals(currentInstance.mSwitchingAwareRotationList.mImeSubtypeList, + switchingAwareImeSubtypes)) { + // Can reuse the current instance. + switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList; + } + if (switchingAwareRotationList == null) { + switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes); + } + } + + StaticRotationList switchingUnawareRotationList = null; + { + final List<ImeSubtypeListItem> switchingUnawareImeSubtypes = filterImeSubtypeList( + sortedEnabledItems, false /* supportsSwitchingToNextInputMethod */); + if (currentInstance != null && + currentInstance.mSwitchingUnawareRotationList != null && + Objects.equals( + currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList, + switchingUnawareImeSubtypes)) { + // Can reuse the current instance. + switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList; + } + if (switchingUnawareRotationList == null) { + switchingUnawareRotationList = + new StaticRotationList(switchingUnawareImeSubtypes); + } + } + + return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList); + } + + private ControllerImpl(final DynamicRotationList switchingAwareRotationList, + final StaticRotationList switchingUnawareRotationList) { + mSwitchingAwareRotationList = switchingAwareRotationList; + mSwitchingUnawareRotationList = switchingUnawareRotationList; + } + + public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi, + InputMethodSubtype subtype) { + if (imi == null) { + return null; + } + if (imi.supportsSwitchingToNextInputMethod()) { + return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi, + subtype); + } else { + return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi, + subtype); + } + } + + public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) { + if (imi == null) { + return; + } + if (imi.supportsSwitchingToNextInputMethod()) { + mSwitchingAwareRotationList.onUserAction(imi, subtype); + } + } + + private static List<ImeSubtypeListItem> filterImeSubtypeList( + final List<ImeSubtypeListItem> items, + final boolean supportsSwitchingToNextInputMethod) { + final ArrayList<ImeSubtypeListItem> result = new ArrayList<>(); + final int ALL_ITEMS_COUNT = items.size(); + for (int i = 0; i < ALL_ITEMS_COUNT; i++) { + final ImeSubtypeListItem item = items.get(i); + if (item.mImi.supportsSwitchingToNextInputMethod() == + supportsSwitchingToNextInputMethod) { + result.add(item); + } + } + return result; + } + } + + private final InputMethodSettings mSettings; + private InputMethodAndSubtypeList mSubtypeList; + private ControllerImpl mController; + private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) { mSettings = settings; resetCircularListLocked(context); @@ -269,19 +468,31 @@ public class InputMethodSubtypeSwitchingController { return new InputMethodSubtypeSwitchingController(settings, context); } - // TODO: write unit tests for this method and the logic that determines the next subtype public void onCommitTextLocked(InputMethodInfo imi, InputMethodSubtype subtype) { - // TODO: Implement this. + if (mController == null) { + if (DEBUG) { + Log.e(TAG, "mController shouldn't be null."); + } + return; + } + mController.onUserActionLocked(imi, subtype); } public void resetCircularListLocked(Context context) { mSubtypeList = new InputMethodAndSubtypeList(context, mSettings); + mController = ControllerImpl.createFrom(mController, + mSubtypeList.getSortedInputMethodAndSubtypeList()); } - public ImeSubtypeListItem getNextInputMethodLocked( - boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { - return getNextInputMethodLockedImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(), - onlyCurrentIme, imi, subtype); + public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi, + InputMethodSubtype subtype) { + if (mController == null) { + if (DEBUG) { + Log.e(TAG, "mController shouldn't be null."); + } + return null; + } + return mController.getNextInputMethod(onlyCurrentIme, imi, subtype); } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes, diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index a56fa36..d66ef83 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -169,6 +169,15 @@ public class ArrayUtils return false; } + public static boolean contains(long[] array, long value) { + for (long element : array) { + if (element == value) { + return true; + } + } + return false; + } + public static long total(long[] array) { long total = 0; for (long value : array) { @@ -229,6 +238,14 @@ public class ArrayUtils return array; } + /** + * Appends a new value to a copy of the array and returns the copy. If + * the value is already present, the original array is returned + * @param cur The original array, or null to represent an empty array. + * @param val The value to add. + * @return A new array that contains all of the values of the original array + * with the new value added, or the original array. + */ public static int[] appendInt(int[] cur, int val) { if (cur == null) { return new int[] { val }; @@ -264,4 +281,48 @@ public class ArrayUtils } return cur; } + + /** + * Appends a new value to a copy of the array and returns the copy. If + * the value is already present, the original array is returned + * @param cur The original array, or null to represent an empty array. + * @param val The value to add. + * @return A new array that contains all of the values of the original array + * with the new value added, or the original array. + */ + public static long[] appendLong(long[] cur, long val) { + if (cur == null) { + return new long[] { val }; + } + final int N = cur.length; + for (int i = 0; i < N; i++) { + if (cur[i] == val) { + return cur; + } + } + long[] ret = new long[N + 1]; + System.arraycopy(cur, 0, ret, 0, N); + ret[N] = val; + return ret; + } + + public static long[] removeLong(long[] cur, long val) { + if (cur == null) { + return null; + } + final int N = cur.length; + for (int i = 0; i < N; i++) { + if (cur[i] == val) { + long[] ret = new long[N - 1]; + if (i > 0) { + System.arraycopy(cur, 0, ret, 0, i); + } + if (i < (N - 1)) { + System.arraycopy(cur, i + 1, ret, i, N - i - 1); + } + return ret; + } + } + return cur; + } } diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index 81e67d8..af966b1 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -57,9 +57,9 @@ public class Protocol { public static final int BASE_DNS_PINGER = 0x00050000; public static final int BASE_NSD_MANAGER = 0x00060000; public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000; - public static final int BASE_CONNECTIVITY_SERVICE = 0x00080000; + public static final int BASE_CONNECTIVITY_MANAGER = 0x00080000; public static final int BASE_NETWORK_AGENT = 0x00081000; public static final int BASE_NETWORK_MONITOR = 0x00082000; - public static final int BASE_CONNECTIVITY_MANAGER = 0x00083000; + public static final int BASE_NETWORK_FACTORY = 0x00083000; //TODO: define all used protocols } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index f446c3a..a1cd7f7 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -57,7 +57,6 @@ LOCAL_SRC_FILES:= \ android_view_KeyEvent.cpp \ android_view_KeyCharacterMap.cpp \ android_view_GraphicBuffer.cpp \ - android_view_GLRenderer.cpp \ android_view_GLES20Canvas.cpp \ android_view_HardwareLayer.cpp \ android_view_ThreadedRenderer.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index e069876..e0c5e96 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -130,7 +130,6 @@ extern int register_android_view_RenderNode(JNIEnv* env); extern int register_android_view_RenderNodeAnimator(JNIEnv* env); extern int register_android_view_GraphicBuffer(JNIEnv* env); extern int register_android_view_GLES20Canvas(JNIEnv* env); -extern int register_android_view_GLRenderer(JNIEnv* env); extern int register_android_view_HardwareLayer(JNIEnv* env); extern int register_android_view_ThreadedRenderer(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); @@ -1214,7 +1213,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_RenderNodeAnimator), REG_JNI(register_android_view_GraphicBuffer), REG_JNI(register_android_view_GLES20Canvas), - REG_JNI(register_android_view_GLRenderer), REG_JNI(register_android_view_HardwareLayer), REG_JNI(register_android_view_ThreadedRenderer), REG_JNI(register_android_view_Surface), diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 7aa241a..928a7f8 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -128,8 +128,7 @@ static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) { static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream, int sampleSize, bool ditherImage) { - SkImageInfo bitmapInfo; - if (!bitmap->asImageInfo(&bitmapInfo)) { + if (kUnknown_SkColorType == bitmap->colorType()) { ALOGW("bitmap has unknown configuration so no memory has been allocated"); return NULL; } @@ -137,9 +136,9 @@ static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream, SkImageRef* pr; // only use ashmem for large images, since mmaps come at a price if (bitmap->getSize() >= 32 * 1024) { - pr = new SkImageRef_ashmem(bitmapInfo, stream, sampleSize); + pr = new SkImageRef_ashmem(bitmap->info(), stream, sampleSize); } else { - pr = new SkImageRef_GlobalPool(bitmapInfo, stream, sampleSize); + pr = new SkImageRef_GlobalPool(bitmap->info(), stream, sampleSize); } pr->setDitherImage(ditherImage); bitmap->setPixelRef(pr)->unref(); diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp index ef57e3d..d17f46c 100644 --- a/core/jni/android/graphics/Camera.cpp +++ b/core/jni/android/graphics/Camera.cpp @@ -3,6 +3,8 @@ #include "SkCamera.h" +#include "GraphicsJNI.h" + static jfieldID gNativeInstanceFieldID; static void Camera_constructor(JNIEnv* env, jobject obj) { @@ -93,7 +95,7 @@ static void Camera_getMatrix(JNIEnv* env, jobject obj, jlong matrixHandle) { } static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) { - SkCanvas* native_canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* native_canvas = GraphicsJNI::getNativeCanvas(canvasHandle); jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID); Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle); v->applyToCanvas((SkCanvas*)native_canvas); diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index f7acbd7..5fca582 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -42,22 +42,26 @@ #include <utils/Log.h> -static uint32_t get_thread_msec() { -#if defined(HAVE_POSIX_CLOCKS) - struct timespec tm; +namespace android { - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm); +// Holds an SkCanvas reference plus additional native data. +class NativeCanvasWrapper { +public: + NativeCanvasWrapper(SkCanvas* canvas) + : mCanvas(canvas) { } - return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000; -#else - struct timeval tv; + SkCanvas* getCanvas() const { + return mCanvas.get(); + } - gettimeofday(&tv, NULL); - return tv.tv_sec * 1000LL + tv.tv_usec / 1000; -#endif -} + void setCanvas(SkCanvas* canvas) { + SkASSERT(canvas); + mCanvas.reset(canvas); + } -namespace android { +private: + SkAutoTUnref<SkCanvas> mCanvas; +}; class ClipCopier : public SkCanvas::ClipVisitor { public: @@ -86,27 +90,30 @@ static jboolean hasNonEmptyClip(const SkCanvas& canvas) { class SkCanvasGlue { public: - static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); - canvas->unref(); + // Get the SkCanvas for a given native handle. + static inline SkCanvas* getNativeCanvas(jlong nativeHandle) { + SkASSERT(nativeHandle); + NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle); + SkCanvas* canvas = wrapper->getCanvas(); + SkASSERT(canvas); + + return canvas; } - static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) { - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + // Construct an SkCanvas from the bitmap. + static SkCanvas* createCanvas(SkBitmap* bitmap) { if (bitmap) { - return reinterpret_cast<jlong>(new SkCanvas(*bitmap)); - } else { - // Create an empty bitmap device to prevent callers from crashing - // if they attempt to draw into this canvas. - SkBitmap emptyBitmap; - return reinterpret_cast<jlong>(new SkCanvas(emptyBitmap)); + return SkNEW_ARGS(SkCanvas, (*bitmap)); } + + // Create an empty bitmap device to prevent callers from crashing + // if they attempt to draw into this canvas. + SkBitmap emptyBitmap; + return new SkCanvas(emptyBitmap); } - static void copyCanvasState(JNIEnv* env, jobject clazz, - jlong srcCanvasHandle, jlong dstCanvasHandle) { - SkCanvas* srcCanvas = reinterpret_cast<SkCanvas*>(srcCanvasHandle); - SkCanvas* dstCanvas = reinterpret_cast<SkCanvas*>(dstCanvasHandle); + // Copy the canvas matrix & clip state. + static void copyCanvasState(SkCanvas* srcCanvas, SkCanvas* dstCanvas) { if (srcCanvas && dstCanvas) { dstCanvas->setMatrix(srcCanvas->getTotalMatrix()); if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) { @@ -116,6 +123,42 @@ public: } } + // Native JNI handlers + static void finalizer(JNIEnv* env, jobject clazz, jlong nativeHandle) { + NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle); + delete wrapper; + } + + // Native wrapper constructor used by Canvas(Bitmap) + static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) { + // No check - 0 is a valid bitmapHandle. + SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); + SkCanvas* canvas = createCanvas(bitmap); + + return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas)); + } + + // Native wrapper constructor used by Canvas(native_canvas) + static jlong initCanvas(JNIEnv* env, jobject, jlong canvasHandle) { + SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + return reinterpret_cast<jlong>(new NativeCanvasWrapper(canvas)); + } + + // Set the given bitmap as the new draw target (wrapped in a new SkCanvas), + // optionally copying canvas matrix & clip state. + static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, + jboolean copyState) { + NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(canvasHandle); + SkCanvas* newCanvas = createCanvas(reinterpret_cast<SkBitmap*>(bitmapHandle)); + NPE_CHECK_RETURN_VOID(env, newCanvas); + + if (copyState == JNI_TRUE) { + copyCanvasState(wrapper->getCanvas(), newCanvas); + } + + // setCanvas() unrefs the old canvas. + wrapper->setCanvas(newCanvas); + } static void freeCaches(JNIEnv* env, jobject) { // these are called in no particular order @@ -163,7 +206,7 @@ public: static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds, jlong paintHandle, jint flags) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkRect* bounds_ = NULL; SkRect storage; @@ -177,7 +220,7 @@ public: static jint saveLayer4F(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t, jfloat r, jfloat b, jlong paintHandle, jint flags) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkRect bounds; bounds.set(l, t, r, b); @@ -188,7 +231,7 @@ public: static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds, jint alpha, jint flags) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkRect* bounds_ = NULL; SkRect storage; if (bounds != NULL) { @@ -203,7 +246,7 @@ public: static jint saveLayerAlpha4F(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t, jfloat r, jfloat b, jint alpha, jint flags) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkRect bounds; bounds.set(l, t, r, b); int result = canvas->saveLayerAlpha(&bounds, alpha, @@ -259,14 +302,14 @@ public: static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); canvas->concat(*matrix); } static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); if (NULL == matrix) { canvas->resetMatrix(); @@ -318,8 +361,8 @@ public: static jboolean clipRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jint op) { + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkRect rect; - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); rect.set(left, top, right, bottom); canvas->clipRect(rect, static_cast<SkRegion::Op>(op)); return hasNonEmptyClip(*canvas); @@ -327,7 +370,7 @@ public: static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle, jint op) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); canvas->clipPath(*reinterpret_cast<SkPath*>(pathHandle), static_cast<SkRegion::Op>(op)); return hasNonEmptyClip(*canvas); @@ -335,7 +378,7 @@ public: static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle, jint op) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle); canvas->clipRegion(*deviceRgn, static_cast<SkRegion::Op>(op)); return hasNonEmptyClip(*canvas); @@ -343,13 +386,13 @@ public: static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle)); } static jboolean quickReject__RectF(JNIEnv* env, jobject, jlong canvasHandle, jobject rect) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkRect rect_; GraphicsJNI::jrectf_to_rect(env, rect, &rect_); bool result = canvas->quickReject(rect_); @@ -358,7 +401,7 @@ public: static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); bool result = canvas->quickReject(*reinterpret_cast<SkPath*>(pathHandle)); return result ? JNI_TRUE : JNI_FALSE; } @@ -366,7 +409,7 @@ public: static jboolean quickReject__FFFF(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, jfloat right, jfloat bottom) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkRect r; r.set(left, top, right, bottom); bool result = canvas->quickReject(r); @@ -375,32 +418,32 @@ public: static void drawRGB(JNIEnv* env, jobject, jlong canvasHandle, jint r, jint g, jint b) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); canvas->drawARGB(0xFF, r, g, b); } static void drawARGB(JNIEnv* env, jobject, jlong canvasHandle, jint a, jint r, jint g, jint b) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); canvas->drawARGB(a, r, g, b); } static void drawColor__I(JNIEnv* env, jobject, jlong canvasHandle, jint color) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); canvas->drawColor(color); } static void drawColor__II(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle); canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode)); } static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); canvas->drawPaint(*paint); } @@ -461,14 +504,14 @@ public: static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY, jfloat stopX, jfloat stopY, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); canvas->drawLine(startX, startY, stopX, stopY, *paint); } static void drawRect__RectFPaint(JNIEnv* env, jobject, jlong canvasHandle, jobject rect, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkRect rect_; GraphicsJNI::jrectf_to_rect(env, rect, &rect_); @@ -478,14 +521,14 @@ public: static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); canvas->drawRectCoords(left, top, right, bottom, *paint); } static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jobject joval, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkRect oval; GraphicsJNI::jrectf_to_rect(env, joval, &oval); @@ -494,7 +537,7 @@ public: static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy, jfloat radius, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); canvas->drawCircle(cx, cy, radius, *paint); } @@ -502,7 +545,7 @@ public: static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jobject joval, jfloat startAngle, jfloat sweepAngle, jboolean useCenter, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkRect oval; GraphicsJNI::jrectf_to_rect(env, joval, &oval); @@ -512,7 +555,7 @@ public: static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); canvas->drawRoundRect(rect, rx, ry, *paint); @@ -520,7 +563,7 @@ public: static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPath* path = reinterpret_cast<SkPath*>(pathHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); canvas->drawPath(*path, *paint); @@ -531,7 +574,7 @@ public: jfloat left, jfloat top, jlong paintHandle, jint canvasDensity, jint screenDensity, jint bitmapDensity) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); @@ -591,7 +634,7 @@ public: jlong bitmapHandle, jobject srcIRect, jobject dstRectF, jlong paintHandle, jint screenDensity, jint bitmapDensity) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkRect dst; @@ -604,7 +647,7 @@ public: jlong bitmapHandle, jobject srcIRect, jobject dstRect, jlong paintHandle, jint screenDensity, jint bitmapDensity) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkRect dst; @@ -616,9 +659,8 @@ public: static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle, jintArray jcolors, jint offset, jint stride, jfloat x, jfloat y, jint width, jint height, - jboolean hasAlpha, jlong paintHandle) - { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + jboolean hasAlpha, jlong paintHandle) { + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); SkBitmap bitmap; bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config : @@ -638,7 +680,7 @@ public: static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, jlong matrixHandle, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); @@ -649,7 +691,7 @@ public: jlong bitmapHandle, jint meshWidth, jint meshHeight, jfloatArray jverts, jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); @@ -759,7 +801,7 @@ public: jintArray jcolors, jint colorIndex, jshortArray jindices, jint indexIndex, jint indexCount, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle); const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); @@ -799,7 +841,7 @@ public: jcharArray text, jint index, jint count, jfloat x, jfloat y, jint flags, jlong paintHandle, jlong typefaceHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); jchar* textArray = env->GetCharArrayElements(text, NULL); @@ -812,7 +854,7 @@ public: jint start, jint end, jfloat x, jfloat y, jint flags, jlong paintHandle, jlong typefaceHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); const jchar* textArray = env->GetStringChars(text, NULL); @@ -939,10 +981,10 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l } static void drawTextRun___CIIIIFFIPaintTypeface( - JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index, - jint count, jint contextIndex, jint contextCount, - jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index, + jint count, jint contextIndex, jint contextCount, + jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) { + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); @@ -953,10 +995,10 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l } static void drawTextRun__StringIIIIFFIPaintTypeface( - JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start, - jint end, jint contextStart, jint contextEnd, - jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + JNIEnv* env, jobject obj, jlong canvasHandle, jstring text, jint start, + jint end, jint contextStart, jint contextEnd, + jfloat x, jfloat y, jint dirFlags, jlong paintHandle, jlong typefaceHandle) { + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); @@ -971,7 +1013,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l static void drawPosText___CII_FPaint(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index, jint count, jfloatArray pos, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL; jsize textCount = text ? env->GetArrayLength(text) : NULL; @@ -1002,7 +1044,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l jlong canvasHandle, jstring text, jfloatArray pos, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); const void* text_ = text ? env->GetStringChars(text, NULL) : NULL; int byteLength = text ? env->GetStringLength(text) : 0; @@ -1032,7 +1074,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index, jint count, jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPath* path = reinterpret_cast<SkPath*>(pathHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); @@ -1045,7 +1087,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject, jlong canvasHandle, jstring text, jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkPath* path = reinterpret_cast<SkPath*>(pathHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); const jchar* text_ = env->GetStringChars(text, NULL); @@ -1084,7 +1126,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkRect r; SkIRect ir; bool result = getHardClipBounds(canvas, &r); @@ -1100,7 +1142,7 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = getNativeCanvas(canvasHandle); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle); *matrix = canvas->getTotalMatrix(); } @@ -1108,8 +1150,9 @@ static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat l static JNINativeMethod gCanvasMethods[] = { {"finalizer", "(J)V", (void*) SkCanvasGlue::finalizer}, - {"initRaster","(J)J", (void*) SkCanvasGlue::initRaster}, - {"copyNativeCanvasState","(JJ)V", (void*) SkCanvasGlue::copyCanvasState}, + {"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster}, + {"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas}, + {"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap}, {"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque}, {"getWidth","()I", (void*) SkCanvasGlue::getWidth}, {"getHeight","()I", (void*) SkCanvasGlue::getHeight}, @@ -1224,4 +1267,11 @@ int register_android_graphics_Canvas(JNIEnv* env) { return result; } +} // namespace android + +// GraphicsJNI helper for external clients. +// We keep the implementation here to avoid exposing NativeCanvasWrapper +// externally. +SkCanvas* GraphicsJNI::getNativeCanvas(jlong nativeHandle) { + return android::SkCanvasGlue::getNativeCanvas(nativeHandle); } diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/DrawFilter.cpp index fbfa2ec..3275875 100644 --- a/core/jni/android/graphics/DrawFilter.cpp +++ b/core/jni/android/graphics/DrawFilter.cpp @@ -30,6 +30,35 @@ namespace android { +// Custom version of SkPaintFlagsDrawFilter that also calls setFilterLevel. +class CompatFlagsDrawFilter : public SkPaintFlagsDrawFilter { +public: + CompatFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags, + SkPaint::FilterLevel desiredLevel) + : SkPaintFlagsDrawFilter(clearFlags, setFlags) + , fDesiredLevel(desiredLevel) { + } + + virtual bool filter(SkPaint* paint, Type type) { + SkPaintFlagsDrawFilter::filter(paint, type); + paint->setFilterLevel(fDesiredLevel); + return true; + } + +private: + const SkPaint::FilterLevel fDesiredLevel; +}; + +// Returns whether flags contains FILTER_BITMAP_FLAG. If flags does, remove it. +static inline bool hadFiltering(jint& flags) { + // Equivalent to the Java Paint's FILTER_BITMAP_FLAG. + static const uint32_t sFilterBitmapFlag = 0x02; + + const bool result = (flags & sFilterBitmapFlag) != 0; + flags &= ~sFilterBitmapFlag; + return result; +} + class SkDrawFilterGlue { public: @@ -40,12 +69,25 @@ public: static jlong CreatePaintFlagsDF(JNIEnv* env, jobject clazz, jint clearFlags, jint setFlags) { - // trim off any out-of-range bits - clearFlags &= SkPaint::kAllFlags; - setFlags &= SkPaint::kAllFlags; - if (clearFlags | setFlags) { - SkDrawFilter* filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags); + // Mask both groups of flags to remove FILTER_BITMAP_FLAG, which no + // longer has a Skia equivalent flag (instead it corresponds to + // calling setFilterLevel), and keep track of which group(s), if + // any, had the flag set. + const bool turnFilteringOn = hadFiltering(setFlags); + const bool turnFilteringOff = hadFiltering(clearFlags); + + SkDrawFilter* filter; + if (turnFilteringOn) { + // Turning filtering on overrides turning it off. + filter = new CompatFlagsDrawFilter(clearFlags, setFlags, + SkPaint::kLow_FilterLevel); + } else if (turnFilteringOff) { + filter = new CompatFlagsDrawFilter(clearFlags, setFlags, + SkPaint::kNone_FilterLevel); + } else { + filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags); + } return reinterpret_cast<jlong>(filter); } else { return NULL; diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index dce185d..64ad223 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -320,7 +320,7 @@ SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { SkASSERT(canvas); SkASSERT(env->IsInstanceOf(canvas, gCanvas_class)); jlong canvasHandle = env->GetLongField(canvas, gCanvas_nativeInstanceID); - SkCanvas* c = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* c = getNativeCanvas(canvasHandle); SkASSERT(c); return c; } @@ -698,7 +698,7 @@ int register_android_graphics_Graphics(JNIEnv* env) "nativeInt", "I"); gCanvas_class = make_globalref(env, "android/graphics/Canvas"); - gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "J"); + gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvasWrapper", "J"); gPaint_class = make_globalref(env, "android/graphics/Paint"); gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "J"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index db7b6d9..73dd11b 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -45,6 +45,7 @@ public: static SkPoint* jpointf_to_point(JNIEnv*, jobject jpointf, SkPoint* point); static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf); + static SkCanvas* getNativeCanvas(jlong nativeHandle); static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas); static SkPaint* getNativePaint(JNIEnv*, jobject paint); static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint); diff --git a/core/jni/android/graphics/MaskFilter.cpp b/core/jni/android/graphics/MaskFilter.cpp index 2113330..b394905 100644 --- a/core/jni/android/graphics/MaskFilter.cpp +++ b/core/jni/android/graphics/MaskFilter.cpp @@ -21,8 +21,7 @@ public: static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) { SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); - SkMaskFilter* filter = SkBlurMaskFilter::Create( - (SkBlurMaskFilter::BlurStyle)blurStyle, sigma); + SkMaskFilter* filter = SkBlurMaskFilter::Create((SkBlurStyle)blurStyle, sigma); ThrowIAE_IfNull(env, filter); return reinterpret_cast<jlong>(filter); } diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 855d267..ab5bdb0 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -119,7 +119,7 @@ public: static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF, jlong bitmapHandle, jlong chunkHandle, jlong paintHandle, jint destDensity, jint srcDensity) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle); const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); @@ -138,7 +138,7 @@ public: static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect, jlong bitmapHandle, jlong chunkHandle, jlong paintHandle, jint destDensity, jint srcDensity) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle); const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp index bac8ef7..a8a3dae 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/core/jni/android/graphics/Picture.cpp @@ -56,7 +56,7 @@ public: static void draw(JNIEnv* env, jobject, jlong canvasHandle, jlong pictureHandle) { - SkCanvas* canvas = reinterpret_cast<SkCanvas*>(canvasHandle); + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle); SkASSERT(canvas); SkASSERT(picture); diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index fcf8f83..ddcc396 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -51,6 +51,8 @@ struct SensorOffsets jfieldID fifoMaxEventCount; jfieldID stringType; jfieldID requiredPermission; + jfieldID maxDelay; + jfieldID isWakeUpSensor; } gSensorOffsets; @@ -78,6 +80,8 @@ nativeClassInit (JNIEnv *_env, jclass _this) sensorOffsets.stringType = _env->GetFieldID(sensorClass, "mStringType", "Ljava/lang/String;"); sensorOffsets.requiredPermission = _env->GetFieldID(sensorClass, "mRequiredPermission", "Ljava/lang/String;"); + sensorOffsets.maxDelay = _env->GetFieldID(sensorClass, "mMaxDelay", "I"); + sensorOffsets.isWakeUpSensor = _env->GetFieldID(sensorClass, "mWakeUpSensor", "Z"); } static jint @@ -112,6 +116,8 @@ nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next) env->SetObjectField(sensor, sensorOffsets.stringType, stringType); env->SetObjectField(sensor, sensorOffsets.requiredPermission, requiredPermission); + env->SetIntField(sensor, sensorOffsets.maxDelay, list->getMaxDelay()); + env->SetBooleanField(sensor, sensorOffsets.isWakeUpSensor, list->isWakeUpSensor()); next++; return size_t(next) < count ? next : 0; } @@ -160,7 +166,8 @@ private: ASensorEvent buffer[16]; while ((n = q->read(buffer, 16)) > 0) { for (int i=0 ; i<n ; i++) { - if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER) { + if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER || + buffer[i].type == SENSOR_TYPE_WAKE_UP_STEP_COUNTER) { // step-counter returns a uint64, but the java API only deals with floats float value = float(buffer[i].u64.step_counter); env->SetFloatArrayRegion(mScratch, 0, 1, &value); @@ -175,11 +182,26 @@ private: gBaseEventQueueClassInfo.dispatchFlushCompleteEvent, buffer[i].meta_data.sensor); } else { + int8_t status; + switch (buffer[i].type) { + case SENSOR_TYPE_ORIENTATION: + case SENSOR_TYPE_MAGNETIC_FIELD: + case SENSOR_TYPE_ACCELEROMETER: + case SENSOR_TYPE_GYROSCOPE: + status = buffer[i].vector.status; + break; + case SENSOR_TYPE_HEART_RATE: + status = buffer[i].heart_rate.status; + break; + default: + status = SENSOR_STATUS_ACCURACY_HIGH; + break; + } env->CallVoidMethod(mReceiverObject, gBaseEventQueueClassInfo.dispatchSensorEvent, buffer[i].sensor, mScratch, - buffer[i].vector.status, + status, buffer[i].timestamp); } if (env->ExceptionCheck()) { diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index c5dd06f..ecdfeb2 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -91,53 +91,9 @@ static struct { } gRectClassInfo; // ---------------------------------------------------------------------------- -// Caching -// ---------------------------------------------------------------------------- - -static void android_view_GLES20Canvas_flushCaches(JNIEnv* env, jobject clazz, - jint mode) { - if (Caches::hasInstance()) { - Caches::getInstance().flush(static_cast<Caches::FlushMode>(mode)); - } -} - -static jboolean android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) { - if (Caches::hasInstance()) { - return Caches::getInstance().init() ? JNI_TRUE : JNI_FALSE; - } - return JNI_FALSE; -} - -static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) { - if (Caches::hasInstance()) { - Caches::getInstance().terminate(); - } -} - -// ---------------------------------------------------------------------------- -// Caching -// ---------------------------------------------------------------------------- - -static void android_view_GLES20Canvas_initAtlas(JNIEnv* env, jobject clazz, - jobject graphicBuffer, jlongArray atlasMapArray, jint count) { - - sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer); - jlong* jAtlasMap = env->GetLongArrayElements(atlasMapArray, NULL); - Caches::getInstance().assetAtlas.init(buffer, jAtlasMap, count); - env->ReleaseLongArrayElements(atlasMapArray, jAtlasMap, 0); -} - -// ---------------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------------- -static jlong android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject clazz) { - RENDERER_LOGD("Create OpenGLRenderer"); - OpenGLRenderer* renderer = new OpenGLRenderer(); - renderer->initProperties(); - return reinterpret_cast<jlong>(renderer); -} - static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject clazz, jlong rendererPtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); @@ -174,10 +130,6 @@ static void android_view_GLES20Canvas_finish(JNIEnv* env, jobject clazz, renderer->finish(); } -static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) { - return Stencil::getStencilSize(); -} - static void android_view_GLES20Canvas_setProperty(JNIEnv* env, jobject clazz, jstring name, jstring value) { if (!Caches::hasInstance()) { @@ -373,7 +325,7 @@ static void android_view_GLES20Canvas_setMatrix(JNIEnv* env, jobject clazz, jlong rendererPtr, jlong matrixPtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); - renderer->setMatrix(matrix); + renderer->setMatrix(matrix ? *matrix : SkMatrix::I()); } static void android_view_GLES20Canvas_getMatrix(JNIEnv* env, jobject clazz, @@ -387,7 +339,7 @@ static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject clazz, jlong rendererPtr, jlong matrixPtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); - renderer->concatMatrix(matrix); + renderer->concatMatrix(*matrix); } // ---------------------------------------------------------------------------- @@ -430,7 +382,7 @@ static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject claz OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); - renderer->drawBitmap(bitmap, matrix, paint); + renderer->drawBitmap(bitmap, *matrix, paint); } static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz, @@ -577,15 +529,6 @@ static void android_view_GLES20Canvas_drawRegionAsRects(JNIEnv* env, jobject cla } } -static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz, - jlong rendererPtr, jfloatArray rects, jint count, jlong paintPtr) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - jfloat* storage = env->GetFloatArrayElements(rects, NULL); - SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); - renderer->drawRects(storage, count, paint); - env->ReleaseFloatArrayElements(rects, storage, 0); -} - static void android_view_GLES20Canvas_drawPoints(JNIEnv* env, jobject clazz, jlong rendererPtr, jfloatArray points, jint offset, jint count, jlong paintPtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); @@ -924,39 +867,6 @@ static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz, renderer->drawLayer(layer, x, y); } -static jboolean android_view_GLES20Canvas_copyLayer(JNIEnv* env, jobject clazz, - jlong layerPtr, jlong bitmapPtr) { - Layer* layer = reinterpret_cast<Layer*>(layerPtr); - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr); - return LayerRenderer::copyLayer(layer, bitmap); -} - -static void android_view_GLES20Canvas_pushLayerUpdate(JNIEnv* env, jobject clazz, - jlong rendererPtr, jlong layerPtr) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - Layer* layer = reinterpret_cast<Layer*>(layerPtr); - renderer->pushLayerUpdate(layer); -} - -static void android_view_GLES20Canvas_cancelLayerUpdate(JNIEnv* env, jobject clazz, - jlong rendererPtr, jlong layerPtr) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - Layer* layer = reinterpret_cast<Layer*>(layerPtr); - renderer->cancelLayerUpdate(layer); -} - -static void android_view_GLES20Canvas_clearLayerUpdates(JNIEnv* env, jobject clazz, - jlong rendererPtr) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - renderer->clearLayerUpdates(); -} - -static void android_view_GLES20Canvas_flushLayerUpdates(JNIEnv* env, jobject clazz, - jlong rendererPtr) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - renderer->flushLayerUpdates(); -} - #endif // USE_OPENGL_RENDERER // ---------------------------------------------------------------------------- @@ -1001,14 +911,7 @@ static JNINativeMethod gMethods[] = { { "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable }, #ifdef USE_OPENGL_RENDERER - { "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches }, - { "nInitCaches", "()Z", (void*) android_view_GLES20Canvas_initCaches }, - { "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches }, - { "nInitAtlas", "(Landroid/view/GraphicBuffer;[JI)V", - (void*) android_view_GLES20Canvas_initAtlas }, - - { "nCreateRenderer", "()J", (void*) android_view_GLES20Canvas_createRenderer }, { "nDestroyRenderer", "(J)V", (void*) android_view_GLES20Canvas_destroyRenderer }, { "nSetViewport", "(JII)V", (void*) android_view_GLES20Canvas_setViewport }, { "nPrepare", "(JZ)I", (void*) android_view_GLES20Canvas_prepare }, @@ -1017,9 +920,6 @@ static JNINativeMethod gMethods[] = { { "nSetProperty", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) android_view_GLES20Canvas_setProperty }, - - { "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize }, - { "nCallDrawGLFunction", "(JJ)I", (void*) android_view_GLES20Canvas_callDrawGLFunction }, { "nSave", "(JI)I", (void*) android_view_GLES20Canvas_save }, @@ -1059,7 +959,6 @@ static JNINativeMethod gMethods[] = { { "nDrawColor", "(JII)V", (void*) android_view_GLES20Canvas_drawColor }, { "nDrawRect", "(JFFFFJ)V", (void*) android_view_GLES20Canvas_drawRect }, { "nDrawRects", "(JJJ)V", (void*) android_view_GLES20Canvas_drawRegionAsRects }, - { "nDrawRects", "(J[FIJ)V", (void*) android_view_GLES20Canvas_drawRects }, { "nDrawRoundRect", "(JFFFFFFJ)V", (void*) android_view_GLES20Canvas_drawRoundRect }, { "nDrawCircle", "(JFFFJ)V", (void*) android_view_GLES20Canvas_drawCircle }, { "nDrawCircle", "(JJJJJ)V", (void*) android_view_GLES20Canvas_drawCircleProps }, @@ -1099,11 +998,6 @@ static JNINativeMethod gMethods[] = { { "nCreateDisplayListRenderer", "()J", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, { "nDrawLayer", "(JJFF)V", (void*) android_view_GLES20Canvas_drawLayer }, - { "nCopyLayer", "(JJ)Z", (void*) android_view_GLES20Canvas_copyLayer }, - { "nClearLayerUpdates", "(J)V", (void*) android_view_GLES20Canvas_clearLayerUpdates }, - { "nFlushLayerUpdates", "(J)V", (void*) android_view_GLES20Canvas_flushLayerUpdates }, - { "nPushLayerUpdate", "(JJ)V", (void*) android_view_GLES20Canvas_pushLayerUpdate }, - { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_GLES20Canvas_cancelLayerUpdate }, { "nGetMaximumTextureWidth", "()I", (void*) android_view_GLES20Canvas_getMaxTextureWidth }, { "nGetMaximumTextureHeight", "()I", (void*) android_view_GLES20Canvas_getMaxTextureHeight }, diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp deleted file mode 100644 index d0269a3..0000000 --- a/core/jni/android_view_GLRenderer.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "GLRenderer" - -#include "jni.h" -#include <nativehelper/JNIHelp.h> -#include <android_runtime/AndroidRuntime.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <EGL/egl_cache.h> - -#include <utils/Timers.h> - -#include <private/hwui/DrawGlInfo.h> - -#include <Caches.h> -#include <Extensions.h> -#include <LayerRenderer.h> -#include <RenderNode.h> - -#ifdef USE_OPENGL_RENDERER - EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); -#endif - -namespace android { - -/** - * Note: OpenGLRenderer JNI layer is generated and compiled only on supported - * devices. This means all the logic must be compiled only when the - * preprocessor variable USE_OPENGL_RENDERER is defined. - */ -#ifdef USE_OPENGL_RENDERER - -// ---------------------------------------------------------------------------- -// Defines -// ---------------------------------------------------------------------------- - -// Debug -#define DEBUG_RENDERER 0 - -// Debug -#if DEBUG_RENDERER - #define RENDERER_LOGD(...) ALOGD(__VA_ARGS__) -#else - #define RENDERER_LOGD(...) -#endif - -// ---------------------------------------------------------------------------- -// Surface and display management -// ---------------------------------------------------------------------------- - -static jboolean android_view_GLRenderer_preserveBackBuffer(JNIEnv* env, jobject clazz) { - EGLDisplay display = eglGetCurrentDisplay(); - EGLSurface surface = eglGetCurrentSurface(EGL_DRAW); - - eglGetError(); - eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); - - EGLint error = eglGetError(); - if (error != EGL_SUCCESS) { - RENDERER_LOGD("Could not enable buffer preserved swap behavior (%x)", error); - } - - return error == EGL_SUCCESS; -} - -static jboolean android_view_GLRenderer_isBackBufferPreserved(JNIEnv* env, jobject clazz) { - EGLDisplay display = eglGetCurrentDisplay(); - EGLSurface surface = eglGetCurrentSurface(EGL_DRAW); - EGLint value; - - eglGetError(); - eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &value); - - EGLint error = eglGetError(); - if (error != EGL_SUCCESS) { - RENDERER_LOGD("Could not query buffer preserved swap behavior (%x)", error); - } - - return error == EGL_SUCCESS && value == EGL_BUFFER_PRESERVED; -} - -// ---------------------------------------------------------------------------- -// Tracing and debugging -// ---------------------------------------------------------------------------- - -static bool android_view_GLRenderer_loadProperties(JNIEnv* env, jobject clazz) { - if (uirenderer::Caches::hasInstance()) { - return uirenderer::Caches::getInstance().initProperties(); - } - return false; -} - -static void android_view_GLRenderer_beginFrame(JNIEnv* env, jobject clazz, - jintArray size) { - - EGLDisplay display = eglGetCurrentDisplay(); - EGLSurface surface = eglGetCurrentSurface(EGL_DRAW); - - if (size) { - EGLint value; - jint* storage = env->GetIntArrayElements(size, NULL); - - eglQuerySurface(display, surface, EGL_WIDTH, &value); - storage[0] = value; - - eglQuerySurface(display, surface, EGL_HEIGHT, &value); - storage[1] = value; - - env->ReleaseIntArrayElements(size, storage, 0); - } - - eglBeginFrame(display, surface); -} - -static jlong android_view_GLRenderer_getSystemTime(JNIEnv* env, jobject clazz) { - if (uirenderer::Extensions::getInstance().hasNvSystemTime()) { - return eglGetSystemTimeNV(); - } - return systemTime(SYSTEM_TIME_MONOTONIC); -} - -static void android_view_GLRenderer_destroyLayer(JNIEnv* env, jobject clazz, - jlong layerPtr) { - using namespace android::uirenderer; - Layer* layer = reinterpret_cast<Layer*>(layerPtr); - LayerRenderer::destroyLayer(layer); -} - -static void android_view_GLRenderer_prepareTree(JNIEnv* env, jobject clazz, - jlong renderNodePtr) { - using namespace android::uirenderer; - RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); - TreeInfo ignoredInfo; - renderNode->prepareTree(ignoredInfo); -} - -static void android_view_GLRenderer_invokeFunctor(JNIEnv* env, jobject clazz, - jlong functorPtr, jboolean hasContext) { - using namespace android::uirenderer; - Functor* functor = reinterpret_cast<Functor*>(functorPtr); - DrawGlInfo::Mode mode = hasContext ? DrawGlInfo::kModeProcess : DrawGlInfo::kModeProcessNoContext; - (*functor)(mode, NULL); -} - -#endif // USE_OPENGL_RENDERER - -// ---------------------------------------------------------------------------- -// Shaders -// ---------------------------------------------------------------------------- - -static void android_view_GLRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, - jstring diskCachePath) { - - const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); - egl_cache_t::get()->setCacheFilename(cacheArray); - env->ReleaseStringUTFChars(diskCachePath, cacheArray); -} - -// ---------------------------------------------------------------------------- -// JNI Glue -// ---------------------------------------------------------------------------- - -const char* const kClassPathName = "android/view/GLRenderer"; - -static JNINativeMethod gMethods[] = { -#ifdef USE_OPENGL_RENDERER - { "isBackBufferPreserved", "()Z", (void*) android_view_GLRenderer_isBackBufferPreserved }, - { "preserveBackBuffer", "()Z", (void*) android_view_GLRenderer_preserveBackBuffer }, - { "loadProperties", "()Z", (void*) android_view_GLRenderer_loadProperties }, - - { "beginFrame", "([I)V", (void*) android_view_GLRenderer_beginFrame }, - - { "getSystemTime", "()J", (void*) android_view_GLRenderer_getSystemTime }, - { "nDestroyLayer", "(J)V", (void*) android_view_GLRenderer_destroyLayer }, - { "nPrepareTree", "(J)V", (void*) android_view_GLRenderer_prepareTree }, - { "nInvokeFunctor", "(JZ)V", (void*) android_view_GLRenderer_invokeFunctor }, -#endif - - { "setupShadersDiskCache", "(Ljava/lang/String;)V", - (void*) android_view_GLRenderer_setupShadersDiskCache }, -}; - -int register_android_view_GLRenderer(JNIEnv* env) { - return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); -} - -}; diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp index 8a426ac..0210bd9 100644 --- a/core/jni/android_view_GraphicBuffer.cpp +++ b/core/jni/android_view_GraphicBuffer.cpp @@ -21,6 +21,7 @@ #include "android_os_Parcel.h" #include "android_view_GraphicBuffer.h" +#include "android/graphics/GraphicsJNI.h" #include <android_runtime/AndroidRuntime.h> @@ -75,7 +76,7 @@ static struct { static struct { jfieldID mSurfaceFormat; - jmethodID safeCanvasSwap; + jmethodID setNativeBitmap; } gCanvasClassInfo; #define GET_INT(object, field) \ @@ -197,12 +198,11 @@ static jboolean android_view_GraphicBuffer_lockCanvas(JNIEnv* env, jobject, } SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat()); - - SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap)); - INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); + INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap)); SkRect clipRect; clipRect.set(rect.left, rect.top, rect.right, rect.bottom); + SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas); nativeCanvas->clipRect(clipRect); if (dirtyRect) { @@ -218,8 +218,7 @@ static jboolean android_view_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobj GraphicBufferWrapper* wrapper = reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle); - SkCanvas* nativeCanvas = SkNEW(SkCanvas); - INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); + INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0); if (wrapper) { status_t status = wrapper->buffer->unlock(); @@ -319,7 +318,7 @@ int register_android_view_GraphicBuffer(JNIEnv* env) { FIND_CLASS(clazz, "android/graphics/Canvas"); GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I"); - GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V"); + GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V"); return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp index 33a2705..879836d 100644 --- a/core/jni/android_view_HardwareLayer.cpp +++ b/core/jni/android_view_HardwareLayer.cpp @@ -43,21 +43,6 @@ using namespace uirenderer; #ifdef USE_OPENGL_RENDERER -static jlong android_view_HardwareLayer_createTextureLayer(JNIEnv* env, jobject clazz) { - Layer* layer = LayerRenderer::createTextureLayer(); - if (!layer) return 0; - - return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer) ); -} - -static jlong android_view_HardwareLayer_createRenderLayer(JNIEnv* env, jobject clazz, - jint width, jint height) { - Layer* layer = LayerRenderer::createRenderLayer(width, height); - if (!layer) return 0; - - return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer) ); -} - static void android_view_HardwareLayer_onTextureDestroyed(JNIEnv* env, jobject clazz, jlong layerUpdaterPtr) { DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); @@ -140,8 +125,6 @@ const char* const kClassPathName = "android/view/HardwareLayer"; static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER - { "nCreateTextureLayer", "()J", (void*) android_view_HardwareLayer_createTextureLayer }, - { "nCreateRenderLayer", "(II)J", (void*) android_view_HardwareLayer_createRenderLayer }, { "nOnTextureDestroyed", "(J)V", (void*) android_view_HardwareLayer_onTextureDestroyed }, { "nPrepare", "(JIIZ)Z", (void*) android_view_HardwareLayer_prepare }, diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 6ae02e0..a590dbf 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -211,8 +211,9 @@ static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); - uint64_t bits = env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits); - if (bits) { + BitSet64 bits = + BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits)); + if (!bits.isEmpty()) { jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisValues)); if (valuesArray) { @@ -221,11 +222,9 @@ static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, uint32_t index = 0; do { - uint32_t axis = __builtin_ctzll(bits); - uint64_t axisBit = 1LL << axis; - bits &= ~axisBit; + uint32_t axis = bits.clearFirstMarkedBit(); outRawPointerCoords->setAxisValue(axis, values[index++]); - } while (bits); + } while (!bits.isEmpty()); env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT); env->DeleteLocalRef(valuesArray); @@ -275,21 +274,19 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation, rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); - const uint64_t unpackedAxisBits = 0 - | (1LL << AMOTION_EVENT_AXIS_X) - | (1LL << AMOTION_EVENT_AXIS_Y) - | (1LL << AMOTION_EVENT_AXIS_PRESSURE) - | (1LL << AMOTION_EVENT_AXIS_SIZE) - | (1LL << AMOTION_EVENT_AXIS_TOUCH_MAJOR) - | (1LL << AMOTION_EVENT_AXIS_TOUCH_MINOR) - | (1LL << AMOTION_EVENT_AXIS_TOOL_MAJOR) - | (1LL << AMOTION_EVENT_AXIS_TOOL_MINOR) - | (1LL << AMOTION_EVENT_AXIS_ORIENTATION); - uint64_t outBits = 0; - uint64_t remainingBits = rawPointerCoords->bits & ~unpackedAxisBits; - if (remainingBits) { - uint32_t packedAxesCount = __builtin_popcountll(remainingBits); + BitSet64 bits = BitSet64(rawPointerCoords->bits); + bits.clearBit(AMOTION_EVENT_AXIS_X); + bits.clearBit(AMOTION_EVENT_AXIS_Y); + bits.clearBit(AMOTION_EVENT_AXIS_PRESSURE); + bits.clearBit(AMOTION_EVENT_AXIS_SIZE); + bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MINOR); + bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR); + bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR); + bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION); + if (!bits.isEmpty()) { + uint32_t packedAxesCount = bits.count(); jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount, outPointerCoordsObj); if (!outValuesArray) { @@ -302,12 +299,10 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer const float* values = rawPointerCoords->values; uint32_t index = 0; do { - uint32_t axis = __builtin_ctzll(remainingBits); - uint64_t axisBit = 1LL << axis; - remainingBits &= ~axisBit; - outBits |= axisBit; + uint32_t axis = bits.clearFirstMarkedBit(); + outBits |= BitSet64::valueForBit(axis); outValues[index++] = rawPointerCoords->getAxisValue(axis); - } while (remainingBits); + } while (!bits.isEmpty()); env->ReleasePrimitiveArrayCritical(outValuesArray, outValues, 0); env->DeleteLocalRef(outValuesArray); diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 6c9d060..11f87cc 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -70,7 +70,7 @@ static struct { static struct { jfieldID mSurfaceFormat; - jmethodID safeCanvasSwap; + jmethodID setNativeBitmap; } gCanvasClassInfo; // ---------------------------------------------------------------------------- @@ -232,10 +232,11 @@ static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, bitmap.setPixels(NULL); } - SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap)); - env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); + env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, + reinterpret_cast<jlong>(&bitmap)); if (dirtyRectPtr) { + SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj); nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) ); } @@ -262,8 +263,7 @@ static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz, } // detach the canvas from the surface - SkCanvas* nativeCanvas = SkNEW(SkCanvas); - env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); + env->CallVoidMethod(canvasObj, gCanvasClassInfo.setNativeBitmap, (jlong)0); // unlock surface status_t err = surface->unlockAndPost(); @@ -375,7 +375,7 @@ int register_android_view_Surface(JNIEnv* env) clazz = env->FindClass("android/graphics/Canvas"); gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I"); - gCanvasClassInfo.safeCanvasSwap = env->GetMethodID(clazz, "safeCanvasSwap", "(JZ)V"); + gCanvasClassInfo.setNativeBitmap = env->GetMethodID(clazz, "setNativeBitmap", "(J)V"); clazz = env->FindClass("android/graphics/Rect"); gRectClassInfo.left = env->GetFieldID(clazz, "left", "I"); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 5a935a9..4594cc3 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -61,6 +61,13 @@ static struct { jfieldID secure; } gPhysicalDisplayInfoClassInfo; +static struct { + jfieldID bottom; + jfieldID left; + jfieldID right; + jfieldID top; +} gRectClassInfo; + // Implements SkMallocPixelRef::ReleaseProc, to delete the screenshot on unref. void DeleteScreenshot(void* addr, void* context) { SkASSERT(addr == ((ScreenshotClient*) context)->getPixels()); @@ -104,25 +111,32 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong nativeObject) { ctrl->decStrong((void *)nativeCreate); } -static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject displayTokenObj, - jint width, jint height, jint minLayer, jint maxLayer, bool allLayers, - bool useIdentityTransform) { +static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, + jobject displayTokenObj, jobject sourceCropObj, jint width, jint height, + jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) { sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); if (displayToken == NULL) { return NULL; } + int left = env->GetIntField(sourceCropObj, gRectClassInfo.left); + int top = env->GetIntField(sourceCropObj, gRectClassInfo.top); + int right = env->GetIntField(sourceCropObj, gRectClassInfo.right); + int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom); + Rect sourceCrop(left, top, right, bottom); + ScreenshotClient* screenshot = new ScreenshotClient(); status_t res; if (width > 0 && height > 0) { if (allLayers) { - res = screenshot->update(displayToken, width, height, useIdentityTransform); - } else { - res = screenshot->update(displayToken, width, height, minLayer, maxLayer, + res = screenshot->update(displayToken, sourceCrop, width, height, useIdentityTransform); + } else { + res = screenshot->update(displayToken, sourceCrop, width, height, + minLayer, maxLayer, useIdentityTransform); } } else { - res = screenshot->update(displayToken, useIdentityTransform); + res = screenshot->update(displayToken, sourceCrop, useIdentityTransform); } if (res != NO_ERROR) { delete screenshot; @@ -174,20 +188,25 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject display GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL); } -static void nativeScreenshot(JNIEnv* env, jclass clazz, - jobject displayTokenObj, jobject surfaceObj, - jint width, jint height, jint minLayer, jint maxLayer, bool allLayers, - bool useIdentityTransform) { +static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, + jobject surfaceObj, jobject sourceCropObj, jint width, jint height, + jint minLayer, jint maxLayer, bool allLayers, bool useIdentityTransform) { sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); if (displayToken != NULL) { sp<Surface> consumer = android_view_Surface_getSurface(env, surfaceObj); if (consumer != NULL) { + int left = env->GetIntField(sourceCropObj, gRectClassInfo.left); + int top = env->GetIntField(sourceCropObj, gRectClassInfo.top); + int right = env->GetIntField(sourceCropObj, gRectClassInfo.right); + int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom); + Rect sourceCrop(left, top, right, bottom); + if (allLayers) { minLayer = 0; maxLayer = -1; } - ScreenshotClient::capture( - displayToken, consumer->getIGraphicBufferProducer(), + ScreenshotClient::capture(displayToken, + consumer->getIGraphicBufferProducer(), sourceCrop, width, height, uint32_t(minLayer), uint32_t(maxLayer), useIdentityTransform); } @@ -563,9 +582,9 @@ static JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeRelease }, {"nativeDestroy", "(J)V", (void*)nativeDestroy }, - {"nativeScreenshot", "(Landroid/os/IBinder;IIIIZZ)Landroid/graphics/Bitmap;", + {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIIIZZ)Landroid/graphics/Bitmap;", (void*)nativeScreenshotBitmap }, - {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;IIIIZZ)V", + {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V", (void*)nativeScreenshot }, {"nativeOpenTransaction", "()V", (void*)nativeOpenTransaction }, @@ -640,6 +659,12 @@ int register_android_view_SurfaceControl(JNIEnv* env) gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F"); gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z"); + jclass rectClazz = env->FindClass("android/graphics/Rect"); + gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I"); + gRectClassInfo.left = env->GetFieldID(rectClazz, "left", "I"); + gRectClassInfo.right = env->GetFieldID(rectClazz, "right", "I"); + gRectClassInfo.top = env->GetFieldID(rectClazz, "top", "I"); + jclass frameStatsClazz = env->FindClass("android/view/FrameStats"); jfieldID undefined_time_nano_field = env->GetStaticFieldID(frameStatsClazz, "UNDEFINED_TIME_NANO", "J"); nsecs_t undefined_time_nano = env->GetStaticLongField(frameStatsClazz, undefined_time_nano_field); diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp index 9258543..c1ab515 100644 --- a/core/jni/android_view_TextureView.cpp +++ b/core/jni/android_view_TextureView.cpp @@ -29,6 +29,8 @@ #include <SkCanvas.h> #include <SkImage.h> +#include "android/graphics/GraphicsJNI.h" + namespace android { // ---------------------------------------------------------------------------- @@ -45,7 +47,7 @@ static struct { static struct { jfieldID mSurfaceFormat; - jmethodID safeCanvasSwap; + jmethodID setNativeBitmap; } gCanvasClassInfo; static struct { @@ -159,12 +161,11 @@ static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject, } SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format); - - SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap)); - INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); + INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(&bitmap)); SkRect clipRect; clipRect.set(rect.left, rect.top, rect.right, rect.bottom); + SkCanvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas); nativeCanvas->clipRect(clipRect); if (dirtyRect) { @@ -178,8 +179,7 @@ static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject, static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject, jlong nativeWindow, jobject canvas) { - SkCanvas* nativeCanvas = SkNEW(SkCanvas); - INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); + INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0); if (nativeWindow) { sp<ANativeWindow> window((ANativeWindow*) nativeWindow); @@ -228,7 +228,7 @@ int register_android_view_TextureView(JNIEnv* env) { FIND_CLASS(clazz, "android/graphics/Canvas"); GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I"); - GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V"); + GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V"); FIND_CLASS(clazz, "android/view/TextureView"); GET_FIELD_ID(gTextureViewClassInfo.nativeWindow, clazz, "mNativeWindow", "J"); diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 1397131..a550649 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -22,6 +22,10 @@ #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <EGL/egl_cache.h> + #include <utils/StrongPointer.h> #include <android_runtime/android_view_Surface.h> #include <system/window.h> @@ -329,6 +333,18 @@ static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject c #endif // ---------------------------------------------------------------------------- +// Shaders +// ---------------------------------------------------------------------------- + +static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, + jstring diskCachePath) { + + const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); + egl_cache_t::get()->setCacheFilename(cacheArray); + env->ReleaseStringUTFChars(diskCachePath, cacheArray); +} + +// ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -361,6 +377,8 @@ static JNINativeMethod gMethods[] = { { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending }, { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo }, #endif + { "setupShadersDiskCache", "(Ljava/lang/String;)V", + (void*) android_view_ThreadedRenderer_setupShadersDiskCache }, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index 230658f..e55e4ea 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -46,6 +46,9 @@ #define LIB_SUFFIX ".so" #define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1) +#define RS_BITCODE_SUFFIX ".bc" +#define RS_BITCODE_SUFFIX_LEN (sizeof(RS_BITCODE_SUFFIX) -1) + #define GDBSERVER "gdbserver" #define GDBSERVER_LEN (sizeof(GDBSERVER) - 1) @@ -486,6 +489,42 @@ com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, j return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch); } +enum bitcode_scan_result_t { + APK_SCAN_ERROR = -1, + NO_BITCODE_PRESENT = 0, + BITCODE_PRESENT = 1, +}; + +static jint +com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode(JNIEnv *env, jclass clazz, + jlong apkHandle) { + ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle); + void* cookie = NULL; + if (!zipFile->startIteration(&cookie)) { + return APK_SCAN_ERROR; + } + + char fileName[PATH_MAX]; + ZipEntryRO next = NULL; + while ((next = zipFile->nextEntry(cookie)) != NULL) { + if (zipFile->getEntryFileName(next, fileName, sizeof(fileName))) { + continue; + } + + const size_t fileNameLen = strlen(fileName); + const char* lastSlash = strrchr(fileName, '/'); + const char* baseName = (lastSlash == NULL) ? fileName : fileName + 1; + if (!strncmp(fileName + fileNameLen - RS_BITCODE_SUFFIX_LEN, RS_BITCODE_SUFFIX, + RS_BITCODE_SUFFIX_LEN) && isFilenameSafe(baseName)) { + zipFile->endIteration(cookie); + return BITCODE_PRESENT; + } + } + + zipFile->endIteration(cookie); + return NO_BITCODE_PRESENT; +} + static jlong com_android_internal_content_NativeLibraryHelper_openApk(JNIEnv *env, jclass, jstring apkPath) { @@ -517,6 +556,8 @@ static JNINativeMethod gMethods[] = { {"nativeFindSupportedAbi", "(J[Ljava/lang/String;)I", (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi}, + {"hasRenderscriptBitcode", "(J)I", + (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode}, }; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6d0b325..fe703b2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -258,6 +258,12 @@ <protected-broadcast android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" /> + <!-- Defined in RestrictionsManager --> + <protected-broadcast + android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" /> + <!-- Defined in RestrictionsManager --> + <protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" /> + <!-- ====================================== --> <!-- Permissions for things that cost money --> <!-- ====================================== --> diff --git a/core/res/res/anim/slide_in_micro.xml b/core/res/res/anim/slide_in_micro.xml new file mode 100644 index 0000000..6320e80 --- /dev/null +++ b/core/res/res/anim/slide_in_micro.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/res/anim/slide_in_micro.xml +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate android:fromXDelta="100%p" android:toXDelta="0" + android:duration="@android:integer/config_mediumAnimTime"/> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" + android:duration="@android:integer/config_mediumAnimTime" /> +</set> diff --git a/core/res/res/anim/slide_out_micro.xml b/core/res/res/anim/slide_out_micro.xml new file mode 100644 index 0000000..4cb6df0 --- /dev/null +++ b/core/res/res/anim/slide_out_micro.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/res/anim/slide_out_micro.xml +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate android:fromXDelta="0" android:toXDelta="100%p" + android:duration="@android:integer/config_mediumAnimTime"/> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" + android:duration="@android:integer/config_mediumAnimTime" /> +</set> diff --git a/core/res/res/drawable-hdpi/work_icon.png b/core/res/res/drawable-hdpi/work_icon.png Binary files differindex e90866b..be8a36e 100644 --- a/core/res/res/drawable-hdpi/work_icon.png +++ b/core/res/res/drawable-hdpi/work_icon.png diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index df59abe..b17b8c6 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -267,7 +267,7 @@ <string name="permdesc_statusBarService" msgid="716113660795976060">"Tillader, at appen er statusbjælken."</string> <string name="permlab_expandStatusBar" msgid="1148198785937489264">"udvid/skjul statuslinje"</string> <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Tillader, at appen kan udvide og skjule statusbjælken."</string> - <string name="permlab_install_shortcut" msgid="4279070216371564234">"installer genveje"</string> + <string name="permlab_install_shortcut" msgid="4279070216371564234">"installere genveje"</string> <string name="permdesc_install_shortcut" msgid="8341295916286736996">"Tillader, at en applikation føjer genveje til startskærmen uden brugerindgriben."</string> <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"afinstaller genveje"</string> <string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Tillader, at applikationen fjerner genveje pÃ¥ startskærmen uden brugerindgriben."</string> @@ -281,7 +281,7 @@ <string name="permdesc_receiveEmergencyBroadcast" msgid="848524070262431974">"Tillader, at appen kan modtage og behandle nødtransmissioner. Denne tilladelse er kun tilgængelig for systemapps."</string> <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"læse Cell Broadcast-beskeder"</string> <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tillader, at appen læser Cell Broadcast-beskeder, der modtages af din enhed. I nogle omrÃ¥der sendes der Cell Broadcast-beskeder for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af ​din ​enhed, nÃ¥r der modtages en Cell Broadcast-besked om en nødsituation."</string> - <string name="permlab_sendSms" msgid="5600830612147671529">"send sms-beskeder"</string> + <string name="permlab_sendSms" msgid="5600830612147671529">"sende sms-beskeder"</string> <string name="permdesc_sendSms" msgid="7094729298204937667">"Tillader, at appen kan sende sms-beskeder. Dette kan resultere i uventede opkrævninger. Skadelige apps kan koste dig penge ved at sende beskeder uden din bekræftelse."</string> <string name="permlab_sendRespondViaMessageRequest" msgid="8713889105305943200">"send hændelser, hvor der skal svares pr. besked"</string> <string name="permdesc_sendRespondViaMessageRequest" msgid="7107648548468778734">"Tillader, at appen kan sende anmodninger til andre apps til beskeder for at hÃ¥ndtere hændelser, hvor der skal svares pr. besked."</string> @@ -295,7 +295,7 @@ <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillader, at appen kan modtage og behandle WAP-beskeder. Denne tilladelse omfatter muligheden for at overvÃ¥ge eller slette de beskeder, der sendes til dig, uden at vise dem til dig."</string> <string name="permlab_getTasks" msgid="6466095396623933906">"hente kørende apps"</string> <string name="permdesc_getTasks" msgid="7454215995847658102">"Tillader, at appen kan hente oplysninger om nuværende og seneste opgaver. Med denne tilladelse kan appen finde oplysninger om, hvilke applikationer der bruges pÃ¥ enheden."</string> - <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommuniker pÃ¥ tværs af brugere"</string> + <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"kommunikere pÃ¥ tværs af brugere"</string> <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Tillader, at appen udfører handlinger pÃ¥ tværs af forskellige brugere pÃ¥ enheden. Ondsindede apps kan bruge dette til at krænke beskyttelsen mellem brugere."</string> <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"fuld licens til at kommunikere pÃ¥ tværs af brugere"</string> <string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Tillader alle mulige former for kommunikation pÃ¥ tværs af brugere."</string> @@ -325,7 +325,7 @@ <string name="permdesc_forceStopPackages" msgid="5253157296183940812">"Tillader, at appen kan tvinge andre apps til at stoppe."</string> <string name="permlab_forceBack" msgid="652935204072584616">"tvinge appen til at lukke"</string> <string name="permdesc_forceBack" msgid="3892295830419513623">"Tillader, at appen kan tvinge enhver aktivitet i forgrunden til at lukke og gÃ¥ tilbage. Bør aldrig være nødvendigt til almindelige apps."</string> - <string name="permlab_dump" msgid="1681799862438954752">"hent intern systemtilstand"</string> + <string name="permlab_dump" msgid="1681799862438954752">"hente intern systemtilstand"</string> <string name="permdesc_dump" msgid="1778299088692290329">"Tillader, at appen kan hente systemets interne tilstand. Ondsindede apps kan hente en lang række fortrolige og beskyttede oplysninger, som de normalt aldrig ville have brug for."</string> <string name="permlab_retrieve_window_content" msgid="8022588608994589938">"hente skærmindhold"</string> <string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"Tillader, at appen kan hente indholdet i det aktive vindue. Ondsindede apps kan hente al indholdet i vinduet og undersøge al dens tekst med undtagelse af adgangskoder."</string> @@ -359,7 +359,7 @@ <string name="permdesc_batteryStats" msgid="5897346582882915114">"Tillader, at en applikation læser de aktuelle data for batteriforbruget. Kan tillade, at applikationen henter detaljerede oplysninger om, hvilke apps du bruger."</string> <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"rediger batteristatistikker"</string> <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Tillader, at appen kan ændre indsamlede batteristatistikker. Anvendes ikke af normale apps."</string> - <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hent statistikker for handlinger i appen"</string> + <string name="permlab_getAppOpsStats" msgid="1508779687436585744">"hente statistikker om handlinger i appen"</string> <string name="permdesc_getAppOpsStats" msgid="6243887041577912877">"Tillader, at appen indhenter statistikker for handlinger i applikationen. Denne handling bruges ikke i almindelige apps."</string> <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"lav ændringer i statistik for handlinger i appen"</string> <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Tillader, at appen kan ændre indsamlede statistikker for handlinger i applikationen. Dette kan ikke bruges af almindelige apps."</string> @@ -474,7 +474,7 @@ <string name="permlab_writeContacts" msgid="5107492086416793544">"ændre dine kontaktpersoner"</string> <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt pÃ¥ din tablet, f.eks. hvor ofte du har ringet til dem, sendt dem en e-mail eller pÃ¥ anden mÃ¥de kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string> <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt pÃ¥ din telefon, f.eks. hvor ofte du har ringet til dem, sendt en e-mail til dem eller pÃ¥ anden mÃ¥de kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string> - <string name="permlab_readCallLog" msgid="3478133184624102739">"læs opkaldsliste"</string> + <string name="permlab_readCallLog" msgid="3478133184624102739">"læse opkaldsliste"</string> <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillader, at appen kan læse din tablets opkaldsliste, f.eks. data om indgÃ¥ende og udgÃ¥ende opkald. Med denne tilladelse kan apps gemme dine opkaldslistedata, og skadelige apps kan dele opkaldslistedata uden din viden."</string> <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Tillader, at appen kan læse telefonens opkaldsliste, f.eks. data om indgÃ¥ende og udgÃ¥ende opkald. Med denne tilladelse kan apps gemme dine opkaldslistedata, og skadelige apps kan dele disse opkaldslistedata uden din viden."</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv opkaldsliste"</string> @@ -486,9 +486,9 @@ <string name="permdesc_writeProfile" product="default" msgid="5552084294598465899">"Tillader, at appen kan ændre eller tilføje oplysninger i din personlige profil, der er gemt pÃ¥ din enhed, f.eks. dit navn eller dine kontaktoplysninger. Dette betyder, at andre apps kan identificere dig og sende profiloplysninger til andre."</string> <string name="permlab_bodySensors" msgid="4871091374767171066">"kropssensorer (f.eks. pulsmÃ¥lere)"</string> <string name="permdesc_bodySensors" product="default" msgid="2998865085124153531">"Tillader, at appen fÃ¥r adgang til data fra sensorer, du bruger til at mÃ¥le, hvad der sker inde i din krop, f.eks. din puls."</string> - <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"læs din sociale strøm"</string> + <string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"læse din sociale strøm"</string> <string name="permdesc_readSocialStream" product="default" msgid="4255706027172050872">"Tillader, at appen kan fÃ¥ adgang til og synkronisere sociale opdateringer fra dig og dine venner. Vær forsigtig, nÃ¥r du deler oplysninger – med denne tilladelse kan appen læse kommunikation mellem dig og dine venner pÃ¥ sociale netværk, uanset fortrolighed. Bemærk! Denne tilladelse hÃ¥ndhæves muligvis ikke pÃ¥ alle sociale netværk."</string> - <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skriv i din sociale strøm"</string> + <string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"skrive i din sociale strøm"</string> <string name="permdesc_writeSocialStream" product="default" msgid="3086557552204114849">"Tillader, at appen kan vise sociale opdateringer fra dine venner. Vær forsigtig, nÃ¥r du deler oplysninger – med denne tilladelse kan appen producere meddelelser, der kan synes at komme fra en ven. Bemærk! Denne tilladelse hÃ¥ndhæves muligvis ikke pÃ¥ alle sociale netværk."</string> <string name="permlab_readCalendar" msgid="5972727560257612398">"læse kalenderbegivenheder og fortrolige oplysninger"</string> <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillader, at appen kan læse alle de kalenderbegivenheder, der er gemt pÃ¥ din tablet, f.eks. venners eller kollegers. Med denne tilladelse kan appen dele eller gemme dine kalenderdata, uanset fortrolighed eller følsomhed."</string> @@ -526,7 +526,7 @@ <string name="permdesc_captureSecureVideoOutput" msgid="2779793064709350289">"Tillader, at appen opfanger og omdirigerer et sikkert videooutput."</string> <string name="permlab_mediaContentControl" msgid="8749790560720562511">"kontrollér medieafspilning og metadataadgang"</string> <string name="permdesc_mediaContentControl" msgid="1637478200272062">"Tillader, at appen styrer medieafspilning og fÃ¥r adgang til medieoplysninger (titel, forfatter...)."</string> - <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skift dine lydindstillinger"</string> + <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skifte dine lydindstillinger"</string> <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Tillader, at appen kan ændre globale lydindstillinger, som f.eks. lydstyrke og hvilken højttaler der bruges til output."</string> <string name="permlab_recordAudio" msgid="3876049771427466323">"optage lyd"</string> <string name="permdesc_recordAudio" msgid="4906839301087980680">"Tillader, at appen kan optage lyd med mikrofonen. Med denne tilladelse kan appen til enhver tid optage lyd uden din bekræftelse."</string> @@ -632,7 +632,7 @@ <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Tillader, at appen kan oprette netværkssockets og bruge tilpassede netværksprotokoller. Browseren og andre applikationer indeholder midler til at sende data til internettet, sÃ¥ med denne tilladelse er der ingen forpligtelse til at sende data til internettet."</string> <string name="permlab_writeApnSettings" msgid="505660159675751896">"ændre/opfange netværksindstillinger og trafik"</string> <string name="permdesc_writeApnSettings" msgid="5333798886412714193">"Tillader, at appen kan ændre netværksindstillinger og opsnappe og inspicere al netværkstrafik, f.eks. for at ændre proxy og port for et adgangspunkt. Ondsindede apps kan overvÃ¥ge, omdirigere eller ændre netværkspakker uden din viden."</string> - <string name="permlab_changeNetworkState" msgid="958884291454327309">"skift netværksforbindelse"</string> + <string name="permlab_changeNetworkState" msgid="958884291454327309">"skifte netværksforbindelse"</string> <string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Tillader, at appen kan ændre netværksforbindelsens tilstand."</string> <string name="permlab_changeTetherState" msgid="5952584964373017960">"skifte forbindelse til netdeling"</string> <string name="permdesc_changeTetherState" msgid="1524441344412319780">"Tillader, at appen kan ændre tilstand for en netværksforbindelse via netdeling."</string> @@ -672,9 +672,9 @@ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Tillader, at en app kan ændre synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. anvendes til at aktivere synkronisering af appen Personer med en konto."</string> <string name="permlab_readSyncStats" msgid="7396577451360202448">"læse synkroniseringsstatistikker"</string> <string name="permdesc_readSyncStats" msgid="1510143761757606156">"Tillader, at en app kan læse synkroniseringsstatistikkerne for en konto, f.eks. historikken for synkroniserede begivenheder og hvor meget data der synkroniseres."</string> - <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læs abonnerede feeds"</string> + <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læse feeds, jeg abonnerer pÃ¥"</string> <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Tillader, at appen kan hente oplysninger om de feeds, der synkroniseres."</string> - <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"skriv abonnerede feeds"</string> + <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"skrive feeds, som jeg abonnerer pÃ¥"</string> <string name="permdesc_subscribedFeedsWrite" msgid="6928930188826089413">"Tillader, at appen kan ændre dine synkroniserede feeds. Ondsindede apps kan ændre dine synkroniserede feeds."</string> <string name="permlab_readDictionary" msgid="4107101525746035718">"læse termer, som du har føjet til ordbogen"</string> <string name="permdesc_readDictionary" msgid="659614600338904243">"Tillader, at appen kan læse alle ord, navne og sætninger, som brugeren har gemt i brugerordbogen."</string> @@ -995,7 +995,7 @@ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillader, at appen kan ændre browserens historik eller de bogmærker, der er gemt pÃ¥ din telefon. Dette kan give appen tilladelse til at slette eller ændre browserdata. Bemærk! Denne tilladelse hÃ¥ndhæves muligvis ikke af tredjepartsbrowsere eller andre applikationer med websøgningsfunktioner."</string> <string name="permlab_setAlarm" msgid="1379294556362091814">"indstille en alarm"</string> <string name="permdesc_setAlarm" msgid="316392039157473848">"Tillader, at appen kan indstille en alarm i en installeret alarmapp. Nogle alarmapps har muligvis ikke denne funktion."</string> - <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføj telefonsvarer"</string> + <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføje telefonsvarer"</string> <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Tillader, at appen kan tilføje beskeder pÃ¥ din telefonsvarer."</string> <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"skifte tilladelser til geografisk placering i Browser"</string> <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angÃ¥ende geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkÃ¥rlige websites."</string> @@ -1275,7 +1275,7 @@ <string name="date_picker_dialog_title" msgid="5879450659453782278">"Angiv dato"</string> <string name="date_time_set" msgid="5777075614321087758">"Angiv"</string> <string name="date_time_done" msgid="2507683751759308828">"Udført"</string> - <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"NYHED! "</font></string> + <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"NY: "</font></string> <string name="perms_description_app" msgid="5139836143293299417">"Leveret af <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="no_permissions" msgid="7283357728219338112">"Der kræves ingen tilladelser"</string> <string name="perm_costs_money" msgid="4902470324142151116">"dette kan koste dig penge"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 989eec6..d0561b9 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1471,8 +1471,8 @@ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string> <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ΕπιλÎξτε κάποια εφαÏμογή"</string> <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Δεν ήταν δυνατή η εκκίνηση του <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινοποίηση με"</string> - <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινοποίηση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> + <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινή χÏήση με"</string> + <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινή χÏήση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="content_description_sliding_handle" msgid="415975056159262248">"Στοιχείο χειÏÎ¹ÏƒÎ¼Î¿Ï Î¼Îµ δυνατότητα ολίσθησης. Αγγίξτε και πατήστε παÏατεταμÎνα."</string> <string name="description_target_unlock_tablet" msgid="3833195335629795055">"ΣÏÏετε για ξεκλείδωμα."</string> <string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"ΣυνδÎστε ακουστικά για να ακοÏσετε τα πλήκτÏα του ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης να εκφωνοÏνται."</string> @@ -1516,7 +1516,7 @@ <string name="sha1_fingerprint" msgid="7930330235269404581">"ΑποτÏπωμα SHA-1"</string> <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Εμφάνιση όλων"</string> <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Επιλογή δÏαστηÏιότητας"</string> - <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινοποίηση με"</string> + <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινή χÏήση με"</string> <string name="list_delimeter" msgid="3975117572185494152">", "</string> <string name="sending" msgid="3245653681008218030">"Γίνεται αποστολή…"</string> <string name="launchBrowserDefault" msgid="2057951947297614725">"Εκκίνηση Ï€ÏογÏάμματος πεÏιήγησης;"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index c8eca25..3d043c4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -294,7 +294,7 @@ <string name="permlab_receiveWapPush" msgid="5991398711936590410">"recevoir des messages texte (WAP)"</string> <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Permet à l\'application de recevoir et de traiter les messages WAP. Cette autorisation lui donne la possibilité de surveiller ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string> <string name="permlab_getTasks" msgid="6466095396623933906">"récupérer les applications en cours d\'exécution"</string> - <string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible de d\'obtenir des informations sur les applications utilisées sur l\'appareil."</string> + <string name="permdesc_getTasks" msgid="7454215995847658102">"Permet à l\'application de récupérer des informations sur des tâches en cours d\'exécution et récemment exécutées. L\'application est ainsi susceptible d\'obtenir des informations sur les applications utilisées sur l\'appareil."</string> <string name="permlab_interactAcrossUsers" msgid="7114255281944211682">"interagir entre les utilisateurs"</string> <string name="permdesc_interactAcrossUsers" msgid="364670963623385786">"Permet à l\'application d\'effectuer des actions entre les différents utilisateurs de l\'appareil. Les applications malveillantes peuvent utiliser cette autorisation pour passer outre la protection entre les utilisateurs."</string> <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"autorisation totale d\'interagir entre les utilisateurs"</string> @@ -503,7 +503,7 @@ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"autoriser l\'installation d\'un fournisseur de services de localisation"</string> <string name="permdesc_installLocationProvider" msgid="9066146120470591509">"Permet de créer des sources de localisation fictives à des fins de tests ou pour installer un nouveau fournisseur de position. L\'application peut ainsi modifier la position et/ou l\'état renvoyé par d\'autres sources de localisation telles que le GPS ou les fournisseurs de position."</string> <string name="permlab_accessFineLocation" msgid="1191898061965273372">"position précise (GPS et réseau)"</string> - <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puissent déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string> + <string name="permdesc_accessFineLocation" msgid="5295047563564981250">"Permet à l\'application d\'obtenir votre position exacte à l\'aide du récepteur satellite GPS ou des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez, le cas échéant. Cette autorisation peut entraîner une utilisation accrue de la batterie."</string> <string name="permlab_accessCoarseLocation" msgid="4887895362354239628">"position approximative (réseau)"</string> <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"Permet à l\'application d\'obtenir votre position approximative. Celle-ci est fournie par des services de localisation sur la base des sources de localisation de réseau tels que les points d\'accès Wi-Fi et les antennes-relais. Ces services de localisation doivent être activés et disponibles sur votre appareil pour que l\'application puisse déterminer où vous vous trouvez de façon approximative, le cas échéant."</string> <string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"Accès à SurfaceFlinger"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 000fb3a..2adaaf1 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -496,7 +496,7 @@ <string name="permlab_writeCalendar" msgid="8438874755193825647">"dodajte ili izmijenite kalendarske dogaÄ‘aje i poÅ¡aljite e-poÅ¡tu gostima bez znanja vlasnika"</string> <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu dogaÄ‘aja koje možete izmijeniti na tabletnom raÄunalu, ukljuÄujući one od vaÅ¡ih prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu dogaÄ‘aja bez znanja vlasnika."</string> <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Aplikaciji omogućuje dodavanje, uklanjanje i promjenu dogaÄ‘aja koje možete izmijeniti na telefonu, ukljuÄujući one od vaÅ¡ih prijatelja ili suradnika. To aplikaciji može omogućiti slanje poruka koje izgledaju kao da dolaze od vlasnika kalendara ili izmjenu dogaÄ‘aja bez znanja vlasnika."</string> - <string name="permlab_accessMockLocation" msgid="8688334974036823330">"omogući testiranje izvora lokacije"</string> + <string name="permlab_accessMockLocation" msgid="8688334974036823330">"omogućeno testiranje izvora lokacije"</string> <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Stvaranje lažnih izvora lokacije radi testiranja ili za instaliranje novog pružatelja usluga lokacije. To aplikaciji omogućuje zaobilaženje lokacije i/ili statusa koji vraćaju drugi izvori lokacije, primjerice GPS ili pružatelji usluga lokacije."</string> <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim naredbama davatelja lokacije"</string> <string name="permdesc_accessLocationExtraCommands" msgid="5945166642335800763">"Aplikaciji omogućuje pristup dodatnim naredbama za pružatelja usluga lokacije. To aplikaciji može omogućiti ometanje rada GPS-a ili drugih izvora lokacije."</string> diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml index dbe2d7f..f3b8495 100644 --- a/core/res/res/values-km-rKH/strings.xml +++ b/core/res/res/values-km-rKH/strings.xml @@ -68,7 +68,7 @@ </plurals> <string name="imei" msgid="2625429890869005782">"IMEI"</string> <string name="meid" msgid="4841221237681254195">"MEID"</string> - <string name="ClipMmi" msgid="6952821216480289285">"áž›áŸážâ€‹ážŸáž˜áŸ’គាល់​អ្នក​ហៅ​​ចូល​"</string> + <string name="ClipMmi" msgid="6952821216480289285">"áž›áŸážâ€‹ážŸáž˜áŸ’គាល់​អ្នក​ហៅ​​ចូល"</string> <string name="ClirMmi" msgid="7784673673446833091">"áž›áŸážâ€‹ážŸáž˜áŸ’គាល់​អ្នក​ហៅ​ចáŸáž‰"</string> <string name="CfMmi" msgid="5123218989141573515">"បញ្ជូន​ការ​ហៅ​បន្áž"</string> <string name="CwMmi" msgid="9129678056795016867">"រង់ចាំ​ការ​ហៅ"</string> @@ -125,7 +125,7 @@ <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិន​បាន​បញ្ជូន​បន្áž"</string> <string name="fcComplete" msgid="3118848230966886575">"កូដ​លក្ážážŽáŸˆâ€‹áž–áŸáž‰áž›áŸáž‰áŸ”"</string> <string name="fcError" msgid="3327560126588500777">"បញ្ហា​ការ​ážáž—្ជាប់​ ឬ​កូដ​លក្ážážŽáŸˆâ€‹â€‹â€‹áž˜áž·áž“​ážáŸ’រឹមážáŸ’រូវ​។"</string> - <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម​"</string> + <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម"</string> <string name="httpError" msgid="7956392511146698522">"មាន​កំហុស​បណ្ដាញ។"</string> <string name="httpErrorLookup" msgid="4711687456111963163">"រក​មិន​ឃើញ URL ។"</string> <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណáŸâ€‹áž•្ទៀងផ្ទាážáŸ‹â€‹ážáŸ†áž”ន់បណ្ដាញ​មិន​ážáŸ’រូវ​បាន​គាំទ្រ។"</string> @@ -183,7 +183,7 @@ <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើក​សំឡáŸáž„"</string> <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"áž–áŸáž›â€‹áž‡áž·áŸ‡â€‹áž™áž“្ážáž ោះ"</string> <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បាន​បើក​របៀប​ពáŸáž›â€‹áž‡áž·áŸ‡â€‹áž™áž“្ážâ€‹áž ោះ"</string> - <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពáŸáž›áž‡áž·áŸ‡â€‹áž™áž“្ážáž ោះ​"</string> + <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពáŸáž›áž‡áž·áŸ‡â€‹áž™áž“្ážáž ោះ"</string> <string name="global_action_settings" msgid="1756531602592545966">"ការ​កំណážáŸ‹"</string> <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string> <string name="safeMode" msgid="2788228061547930246">"របៀប​​​សុវážáŸ’ážáž·áž—áž¶áž–"</string> @@ -195,7 +195,7 @@ <string name="permgrouplab_messages" msgid="7521249148445456662">"សារ​របស់​អ្នក"</string> <string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និង​សរសáŸážšâ€‹ážŸáž¶ážš SMS, អ៊ីមែល និង​សារ​ផ្សáŸáž„ៗ​ទៀážâ€‹ážšáž”ស់​អ្នក។"</string> <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"áž–áŸážáŸŒáž˜áž¶áž“​ផ្ទាល់ážáŸ’លួន​របស់​អ្នក"</string> - <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ពáŸážáŸŒáž˜áž¶áž“​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាážâ€‹áž‘ំនាក់ទំនង​របស់​អ្នក។​"</string> + <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ពáŸážáŸŒáž˜áž¶áž“​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាážâ€‹áž‘ំនាក់ទំនង​របស់​អ្នក។"</string> <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"áž–áŸážáŸŒáž˜áž¶áž“​សង្គម​របស់​អ្នក"</string> <string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូល​ដំណើរការ​ពáŸážáŸŒáž˜áž¶áž“​ដោយ​ផ្ទាល់​អំពី​ទំនាក់ទំនង និង​ការ​ភ្ជាប់​សង្គម​របស់​អ្នក។"</string> <string name="permgrouplab_location" msgid="635149742436692049">"ទីážáž¶áŸ†áž„​របស់​អ្នក"</string> @@ -384,7 +384,7 @@ <string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យ​កម្មវិធី​មើល​គ្រាប់​ចុច​ដែល​អ្នក​ចុច​ពáŸáž›â€‹áž˜áž¶áž“​អន្ážážšáž€áž˜áŸ’ម​ជា​មួយ​កម្មវិធី​ផ្សáŸáž„ (ដូចជា បញ្ចូល​ពាក្យ​សម្ងាážáŸ‹)។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> <string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចង​ទៅ​វិធីសាស្ážáŸ’រ​បញ្ចូល"</string> <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិážâ€‹áž€áŸ†áž–ូល​នៃ​វិធី​សាស្ážáŸ’រ​បញ្ចូល។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> - <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សáŸážœáž¶â€‹áž€áž˜áŸ’ម​ភាព​មធ្យោបាយ​ងាយស្រួល​"</string> + <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សáŸážœáž¶â€‹áž€áž˜áŸ’ម​ភាព​មធ្យោបាយ​ងាយស្រួល"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យ​​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិážâ€‹áž€áŸ†áž–ូល​នៃ​សáŸážœáž¶áž€áž˜áŸ’ម​ភាព​ងាយស្រួល។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> <string name="permlab_bindPrintService" msgid="8462815179572748761">"ចង​សáŸážœáž¶áž€áž˜áŸ’ម​​បោះពុម្ព"</string> <string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិážâ€‹áž€áŸ†áž–ូល​នៃ​សáŸážœáž¶áž€áž˜áŸ’ម​ធាážáž»â€‹áž€áŸ’រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> @@ -402,7 +402,7 @@ <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"អនុញ្ញាážâ€‹áž²áŸ’យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុច​ប្រទាក់​កម្រិážâ€‹áž€áŸ†áž–ូល​​របស់​សáŸážœáž¶áž€áž˜áŸ’ម​អន្ážážšáž€áž˜áŸ’ម​សំឡáŸáž„។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់​ទៅ​ការ​បង្ហាញ​ពី​ចម្ងាយ"</string> <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាážâ€‹áž²áŸ’យ​ម្ចាស់​ភ្ជាប់​​ទៅ​ចំណុច​ប្រទាក់​កម្រិážâ€‹áž€áŸ†áž–ូល​នៃ​ការ​បង្ហាញ​ពី​ចម្ងាយ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> - <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សáŸážœáž¶â€‹áž€áž˜áŸ’ម​ធាážáž»â€‹áž€áŸ’រាហ្វិក​"</string> + <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សáŸážœáž¶â€‹áž€áž˜áŸ’ម​ធាážáž»â€‹áž€áŸ’រាហ្វិក"</string> <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិážâ€‹áž€áŸ†áž–ូល​នៃ​សáŸážœáž¶áž€áž˜áŸ’ម​ធាážáž»â€‹áž€áŸ’រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់​ទៅ​សáŸážœáž¶áž€áž˜áŸ’ម​ក្រុមហ៊ុន​ផ្ដល់​ច្រក"</string> <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាážâ€‹áž²áŸ’យ​ម្ចាស់​ភ្ជាប់​ទៅ​ក្រុមហ៊ុន​ផ្ដល់​​ច្រក​ដែល​បាន​ចុះ​ឈ្មោះ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> @@ -410,7 +410,7 @@ <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យ​ម្ចាស់​ផ្ញើ​គោលបំណង​​ទៅ​អ្នក​គ្រប់គ្រង​ឧបករណáŸáŸ” មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> <string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់​ទៅ​ការ​បញ្ចូល​ទូរទស្សនáŸ"</string> <string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាážâ€‹áž²áŸ’យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុចប្រទាក់​កម្រិážâ€‹ážáŸ’ពស់​នៃ​ការ​បញ្ចូល​ទូរទស្សនáŸáŸ” មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> - <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ážáŸ‚ម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណáŸâ€‹â€‹"</string> + <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ážáŸ‚ម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណáŸ"</string> <string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាážâ€‹â€‹â€‹áž²áŸ’យ​ម្ចាស់​​​បន្ážáŸ‚ម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​ឧបករណáŸâ€‹ážŸáž€áž˜áŸ’ម​ចáŸáž‰â€‹áŸ” មិន​គួរ​ប្រើ​សម្រាប់​កម្មវិធី​​ធម្មážáž¶â€‹áž‘áŸâ€‹áŸ”"</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរ​ទិស​អáŸáž€áŸ’រង់"</string> <string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យ​កម្មវិធី​ប្ដូរ​ការ​បង្វិល​អáŸáž€áŸ’រង់​នៅ​ពáŸáž›â€‹ážŽáž¶â€‹áž˜áž½áž™áŸ” មិន​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មážáž¶â€‹áž‘áŸáŸ”"</string> @@ -422,9 +422,9 @@ <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យ​កម្មវិធី​ស្នើ​​សញ្ញា​ដែល​បាន​ផ្ដល់​ážáŸ’រូវ​ផ្ញើ​ទៅ​ដំណើរការ​ស្ážáž·ážážŸáŸ’ážáŸážšâ€‹â€‹áž‘ាំង​អស់។"</string> <string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើ​ឲ្យ​កម្មវិធី​ដំណើរការ​ជា​និច្ច"</string> <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យ​កម្មវិធី​ធ្វើជា​ផ្នែក​​ស្ážáž·ážážŸáŸ’ážáŸážšâ€‹ážŠáŸ„យ​ážáŸ’លួន​ឯង​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កំណážáŸ‹â€‹áž¢áž„្គ​ចងចាំ​ដែល​អាច​ប្រើ​បាន​ចំពោះ​កម្មវិធី​ផ្សáŸáž„ៗ​ ដោយ​ធ្វើឲ្យ​កុំព្យូទáŸážšâ€‹áž”ន្ទះ​យឺážáŸ”"</string> - <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ážáŸ’រៃយáŸâ€‹áž“ៃ​ážáŸ’លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិážâ€‹áž¢áž„្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សáŸáž„​ធ្វើ​ឲ្យ​ទូរសáŸáž–្ទ​របស់​អ្នក​យឺážáŸ”​"</string> + <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ážáŸ’រៃយáŸâ€‹áž“ៃ​ážáŸ’លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិážâ€‹áž¢áž„្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សáŸáž„​ធ្វើ​ឲ្យ​ទូរសáŸáž–្ទ​របស់​អ្នក​យឺážáŸ”"</string> <string name="permlab_deletePackages" msgid="184385129537705938">"លុប​កម្មវិធី"</string> - <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំážáž¶áž“់​ៗ។ ​"</string> + <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំážáž¶áž“់​ៗ។"</string> <string name="permlab_clearAppUserData" msgid="274109191845842756">"លុប​ទិន្ននáŸáž™â€‹ážšáž”ស់​​កម្មវិធី​ផ្សáŸáž„"</string> <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យ​កម្មវិធី​សម្អាážâ€‹áž‘ិន្ននáŸáž™â€‹áž¢áŸ’នក​ប្រើ។"</string> <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុប​ឃ្លាំង​សម្ងាážáŸ‹â€‹áž€áž˜áŸ’មវិធី​ផ្សáŸáž„ៗ"</string> @@ -475,7 +475,7 @@ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យ​កម្មវិធី​កែ​ទិន្ននáŸáž™â€‹áž¢áŸ†áž–ី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​កុំព្យូទáŸážšâ€‹áž”ន្ទះ រួមមាន​ប្រáŸáž€áž„់​​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​ទាក់ទង​ážáž¶áž˜â€‹ážœáž·áž’ី​ផ្សáŸáž„ៗ​ជា​មួយ​ទំនាក់ទំនង​ជាក់លាក់។ សិទ្ធិ​​នáŸáŸ‡â€‹áž¢áž“ុញ្ញាážâ€‹áž²áŸ’យ​​​កម្មវិធី​លុប​ទិន្ននáŸáž™â€‹áž‘ំនាក់ទំនង​របស់​អ្នក។"</string> <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យ​កម្មវិធី​កែ​ទិន្ននáŸáž™â€‹áž¢áŸ†áž–ី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​ទូរសáŸáž–្ទ​របស់​អ្នក រួមមាន​ប្រáŸáž€áž„់​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​បាន​ទាក់ទង​​ážáž¶áž˜â€‹ážœáž·áž’ី​ផ្សáŸáž„ៗ​ជា​មួយ​ទំនាក់​ទំនាក់​ជាក់លាក់។ សិទ្ធិ​នáŸáŸ‡â€‹áž²áŸ’យ​កម្មវិធី​លុប​ទិន្ននáŸáž™â€‹áž‘ំនាក់ទំនង។"</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"អាន​​កំណážáŸ‹â€‹áž áŸážáž»â€‹â€‹â€‹áž ៅ"</string> - <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទáŸážšâ€‹áž”ន្ទះ​របស់​អ្នក រួមមាន​ទិន្ននáŸáž™â€‹áž¢áŸ†áž–ី​ការ​ហៅ​ចូល និង​ចáŸáž‰áŸ” សិទ្ធិ​នáŸáŸ‡â€‹áž¢áž“ុញ្ញាážâ€‹áž²áŸ’យ​កម្មវិធី​រក្សាទុក​ទិន្ននáŸáž™â€‹áž”ញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្ននáŸáž™â€‹áž”ញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។​"</string> + <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទáŸážšâ€‹áž”ន្ទះ​របស់​អ្នក រួមមាន​ទិន្ននáŸáž™â€‹áž¢áŸ†áž–ី​ការ​ហៅ​ចូល និង​ចáŸáž‰áŸ” សិទ្ធិ​នáŸáŸ‡â€‹áž¢áž“ុញ្ញាážâ€‹áž²áŸ’យ​កម្មវិធី​រក្សាទុក​ទិន្ននáŸáž™â€‹áž”ញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្ននáŸáž™â€‹áž”ញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។"</string> <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យ​កម្មវិធី​អាន​​​បញ្ជី​ហៅ​ទូរសáŸáž–្ទ​របស់​អ្នក រួមមាន​ទិន្ននáŸáž™â€‹áž¢áŸ†áž–ី​ការ​ហៅ​ចូល និង​ចáŸáž‰áŸ” សិទ្ធិ​នáŸáŸ‡â€‹áž¢áž“ុញ្ញាážâ€‹áž²áŸ’យ​កម្មវិធី​រក្សាទុក​ទិន្ននáŸáž™â€‹áž”ញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្ននáŸáž™â€‹áž”ញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នកដឹង។"</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសáŸážšâ€‹áž”ញ្ជី​ហៅ"</string> <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យ​កម្មវិធី​កែ​បញ្ជី​ហៅ​កុំព្យូទáŸážšâ€‹áž”ន្ទះ​របស់​អ្នក​រួមមាន​ទិន្ននáŸáž™â€‹áž¢áŸ†áž–ី​ការ​ហៅ​ចូល និង​ចáŸáž‰áŸ”​កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប ឬ​កែ​បញ្ជី​ហៅ​របស់​អ្នក។"</string> @@ -609,7 +609,7 @@ <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យ​កម្មវិធី​កំណážáŸ‹â€‹áž‡áŸ†áž“ួយ​ទំហំ​ផ្ទាំង​រូបភាព​ប្រពáŸáž“្ធ។"</string> <string name="permlab_masterClear" msgid="2315750423139697397">"កំណážáŸ‹â€‹áž”្រពáŸáž“្ធ​ទៅ​លំនាំដើម​រោងចក្រ​ឡើងវិញ"</string> <string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យ​កម្មវិធី​កំណážáŸ‹â€‹áž”្រពáŸáž“្ធ​​ដូច​ការ​កំណážáŸ‹â€‹áž…áŸáž‰â€‹áž–ី​រោងចក្រ​ឡើងវិញ​ពáŸáž‰áž›áŸáž‰ ដោយ​លុប​ទិន្ននáŸáž™ ការ​កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ និង​កម្មវិធី​បាន​ដំឡើង។"</string> - <string name="permlab_setTime" msgid="2021614829591775646">"កំណážáŸ‹â€‹â€‹áž˜áŸ‰áŸ„ង​"</string> + <string name="permlab_setTime" msgid="2021614829591775646">"កំណážáŸ‹â€‹â€‹áž˜áŸ‰áŸ„áž„"</string> <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​កុំព្យូទáŸážšâ€‹áž”ន្ទះ។"</string> <string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​ទូរសáŸáž–្ទ។"</string> <string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណážáŸ‹â€‹ážáŸ†áž”ន់​ពáŸáž›ážœáŸáž›áž¶"</string> @@ -775,7 +775,7 @@ <string-array name="organizationTypes"> <item msgid="7546335612189115615">"កន្លែង​ធ្វើការ"</item> <item msgid="4378074129049520373">"ផ្សáŸáž„ៗ"</item> - <item msgid="3455047468583965104">"ážáž¶áž˜â€‹ážáž˜áŸ’រូវ​ការ​"</item> + <item msgid="3455047468583965104">"ážáž¶áž˜â€‹ážáž˜áŸ’រូវ​ការ"</item> </string-array> <string-array name="imProtocols"> <item msgid="8595261363518459565">"AIM"</item> @@ -791,7 +791,7 @@ <string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string> <string name="phoneTypeMobile" msgid="6501463557754751037">"​ចលáŸáž"</string> <string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែង​ធ្វើការ"</string> - <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ​"</string> + <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ"</string> <string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារ​ផ្ទះ"</string> <string name="phoneTypePager" msgid="7582359955394921732">"áž—áŸáž™áŸážš"</string> <string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សáŸáž„ៗ"</string> @@ -916,7 +916,7 @@ <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាម​លំនាំ​ច្រើន​ពáŸáž€"</string> <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បី​ដោះ​សោ ចូល​គណនី Google របស់​អ្នក។"</string> <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ីមែល​)"</string> - <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាážáŸ‹â€‹"</string> + <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាážáŸ‹"</string> <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string> <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាážáŸ‹â€‹áž˜áž·áž“​ážáŸ’រឹមážáŸ’រូវ។"</string> <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លáŸáž…​ឈ្មោះ​អ្នក​ប្រើ ឬ​ពាក្យ​សម្ងាážáŸ‹â€‹ážšáž”ស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string> @@ -961,7 +961,7 @@ <string name="factorytest_failed" msgid="5410270329114212041">"បាន​បរាជáŸáž™â€‹áž€áŸ’នុង​ការ​សាកល្បង​រោងចក្រ"</string> <string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ážáŸ’រូវ​បាន​គាំទ្រ​សម្រាប់​ážáŸ‚​កញ្ចប់​បាន​ដំឡើង​ក្នុង /system/app."</string> <string name="factorytest_no_action" msgid="872991874799998561">"រក​មិន​ឃើញ​កញ្ចប់​ដែល​ផ្ដល់​សកម្មភាព FACTORY_TEST ។"</string> - <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ​"</string> + <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ"</string> <string name="js_dialog_title" msgid="1987483977834603872">"ទំពáŸážšâ€‹áž˜áž¶áž“​ចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសáŸážšáŸ–"</string> <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string> <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់​ការ​រុករក"</string> @@ -1019,7 +1019,7 @@ <string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string> <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string> <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string> - <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប​"</string> + <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string> <string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string> <string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string> <string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរក​សំណួរ"</string> @@ -1103,18 +1103,18 @@ <string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string> <string name="preposition_for_time" msgid="5506831244263083793">"នៅ​ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> <string name="preposition_for_year" msgid="5040395640711867177">"ក្នុង​ឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string> - <string name="day" msgid="8144195776058119424">"ážáŸ’ងៃ​"</string> + <string name="day" msgid="8144195776058119424">"ážáŸ’ងៃ"</string> <string name="days" msgid="4774547661021344602">"​ážáŸ’ងៃ"</string> <string name="hour" msgid="2126771916426189481">"ម៉ោង"</string> <string name="hours" msgid="894424005266852993">"ម៉ោង"</string> - <string name="minute" msgid="9148878657703769868">"នាទី​"</string> + <string name="minute" msgid="9148878657703769868">"នាទី"</string> <string name="minutes" msgid="5646001005827034509">"នាទី"</string> - <string name="second" msgid="3184235808021478">"វិនាទី​"</string> + <string name="second" msgid="3184235808021478">"វិនាទី"</string> <string name="seconds" msgid="3161515347216589235">"វិនាទី"</string> - <string name="week" msgid="5617961537173061583">"សប្ដាហáŸâ€‹"</string> - <string name="weeks" msgid="6509623834583944518">"សប្ដាហáŸâ€‹"</string> - <string name="year" msgid="4001118221013892076">"ឆ្នាំ​"</string> - <string name="years" msgid="6881577717993213522">"ឆ្នាំ​"</string> + <string name="week" msgid="5617961537173061583">"សប្ដាហáŸ"</string> + <string name="weeks" msgid="6509623834583944518">"សប្ដាហáŸ"</string> + <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string> + <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string> <plurals name="duration_seconds"> <item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item> <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item> @@ -1130,12 +1130,12 @@ <string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហា​វីដáŸáž¢áž¼"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដáŸáž¢áž¼â€‹áž“áŸáŸ‡â€‹áž˜áž·áž“​ážáŸ’រឹមážáŸ’រូវ​សម្រាប់​​ចរន្ážâ€‹áž…ូល​ឧបករណáŸâ€‹áž“áŸáŸ‡áŸ”"</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិន​អាច​ចាក់​វីដáŸáž¢áž¼â€‹áž“áŸáŸ‡áŸ”"</string> - <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម​"</string> + <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម"</string> <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="noon" msgid="7245353528818587908">"រសៀល"</string> <string name="Noon" msgid="3342127745230013127">"រសៀល"</string> <string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាážáŸ’ážš"</string> - <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាážáŸ’រ​"</string> + <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាážáŸ’ážš"</string> <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll" msgid="6876518925844129331">"ជ្រើស​ទាំងអស់"</string> @@ -1152,13 +1152,13 @@ <string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ážáŸ’រ​បញ្ចូល"</string> <string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាព​អážáŸ’ážáž”áž‘"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់​ទំហំ​ផ្ទុក"</string> - <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុážáž„ារ​ប្រពáŸáž“្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string> + <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុážáž„ារ​ប្រពáŸáž“្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ"</string> <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុង​ដំណើរការ"</string> <string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ​ ដើម្បី​មើល​ពáŸážáŸŒáž˜áž¶áž“​បន្ážáŸ‚ម ឬ​បញ្ឈប់​កម្មវិធី។"</string> - <string name="ok" msgid="5970060430562524910">"យល់​ព្រម​"</string> - <string name="cancel" msgid="6442560571259935130">"បោះ​បង់​"</string> - <string name="yes" msgid="5362982303337969312">"យល់​ព្រម​"</string> - <string name="no" msgid="5141531044935541497">"បោះ​បង់​"</string> + <string name="ok" msgid="5970060430562524910">"យល់​ព្រម"</string> + <string name="cancel" msgid="6442560571259935130">"បោះ​បង់"</string> + <string name="yes" msgid="5362982303337969312">"យល់​ព្រម"</string> + <string name="no" msgid="5141531044935541497">"បោះ​បង់"</string> <string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយáŸážáŸ’áž“"</string> <string name="loading" msgid="7933681260296021180">"កំពុង​ផ្ទុក..."</string> <string name="capital_on" msgid="1544682755514494298">"បើក"</string> @@ -1167,7 +1167,7 @@ <string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើស​កម្មវិធី​ដើម"</string> <string name="alwaysUse" msgid="4583018368000610438">"ប្រើ​ážáž¶áž˜â€‹áž›áŸ†áž“ាំដើម​សម្រាប់​សកម្មភាព​នáŸáŸ‡áŸ”"</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាážâ€‹áž›áŸ†áž“ាំដើម​ក្នុង​ការកំណážáŸ‹â€‹áž”្រពáŸáž“្ធ > កម្មវិធី > ទាញ​យក។"</string> - <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព​​"</string> + <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព"</string> <string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើស​កម្មវិធី​សម្រាប់​ឧបករណáŸâ€‹áž™áž¼áž¢áŸážŸáž”៊ី"</string> <string name="noApplications" msgid="2991814273936504689">"គ្មាន​កម្មវិធី​អាច​អនុវážáŸ’ážâ€‹ážŸáž€áž˜áŸ’មភាព​នáŸáŸ‡áŸ”"</string> <string name="aerr_title" msgid="1905800560317137752"></string> @@ -1178,7 +1178,7 @@ <string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិន​ឆ្លើយážáž”។\n\nážáž¾â€‹áž¢áŸ’នក​ចង់​បិទ​វា?"</string> <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិន​ឆ្លើយážáž”។ ážáž¾â€‹áž¢áŸ’នក​ចង់​បិទ​វា?"</string> <string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិន​ឆ្លើយážáž”។ \n\nážáž¾â€‹áž¢áŸ’នក​ចង់​បិទ​វា​ឬ?"</string> - <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម​"</string> + <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម"</string> <string name="report" msgid="4060218260984795706">"រាយការណáŸ"</string> <string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string> <string name="webpage_unresponsive" msgid="3272758351138122503">"ទំពáŸážšâ€‹áž€áŸ’លាយ​ជា​មិន​ឆ្លើយážáž”។\n\nážáž¾â€‹áž¢áŸ’នក​​ចង់​បិទ​វា?"</string> @@ -1260,19 +1260,19 @@ <string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"áž“áŸáŸ‡â€‹áž¢áž¶áž…​កាážáŸ‹â€‹áž›áž»áž™"</font>" លើ​គណនី​ចលáŸážâ€‹ážšáž”ស់​អ្នក។"</string> <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វា​នឹង​គិážâ€‹ážáŸ’លៃ​សáŸážœáž¶áž€áž˜áŸ’ម​លើ​គណនី​ចលáŸážâ€‹ážšáž”ស់​អ្នក។"</font></string> <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string> - <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់​"</string> + <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់"</string> <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំ​ជម្រើស​របស់​ážáŸ’ញុំ"</string> <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នក​អាច​ប្ដូរ​វា​ពáŸáž›â€‹áž€áŸ’រោយ​ក្នុង​ការ​កំណážáŸ‹ > កម្មវិធី"</string> <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាážâ€‹áž‡áž¶â€‹áž“ិច្ច"</string> <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំ​អនុញ្ញាáž"</string> <string name="sim_removed_title" msgid="6227712319223226185">"បាន​ដក​ស៊ីម​កាážâ€‹áž…áŸáž‰"</string> - <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចលáŸážâ€‹áž“ឹង​ប្រើ​លែង​បាន​រហូážâ€‹ážŠáž›áŸ‹â€‹áž¢áŸ’នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាážâ€‹ážŠáŸ‚ល​បា​បញ្ចូល​ážáŸ’រឹមážáŸ’រូវ។​"</string> + <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចលáŸážâ€‹áž“ឹង​ប្រើ​លែង​បាន​រហូážâ€‹ážŠáž›áŸ‹â€‹áž¢áŸ’នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាážâ€‹ážŠáŸ‚ល​បា​បញ្ចូល​ážáŸ’រឹមážáŸ’រូវ។"</string> <string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string> <string name="sim_added_title" msgid="3719670512889674693">"បាន​បន្ážáŸ‚ម​ស៊ីម​កាáž"</string> <string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើម​ឧបករណáŸâ€‹ážšáž”ស់​អ្នក​ឡើង​វិញ ដើម្បី​ចូល​ដំណើរការ​បណ្ដាញ​ចលáŸážáŸ”"</string> <string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើម​ឡើងវិញ"</string> - <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណážáŸ‹â€‹áž˜áŸ‰áŸ„ង​"</string> - <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណážáŸ‹â€‹áž€áž¶áž›â€‹áž”រិច្ឆáŸáž‘​"</string> + <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណážáŸ‹â€‹áž˜áŸ‰áŸ„áž„"</string> + <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណážáŸ‹â€‹áž€áž¶áž›â€‹áž”រិច្ឆáŸáž‘"</string> <string name="date_time_set" msgid="5777075614321087758">"កំណážáŸ‹"</string> <string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string> <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ážáŸ’មី៖ "</font></string> @@ -1350,7 +1350,7 @@ <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យ​កម្មវិធី​ដក​សáŸážœáž¶áž€áž˜áŸ’ម​នៃ​កម្មវិធី​ផ្ទុក​​លំនាំដើម ដើម្បី​ចម្លង​មាážáž·áž€áž¶áŸ”​ មិន​សម្រាប់​ប្រើ​ដោយ​កម្មវិធី​លំនាំដើម។"</string> <string name="permlab_route_media_output" msgid="1642024455750414694">"នាំ​ផ្លូវ​លទ្ធផល​មáŸážŒáŸ€"</string> <string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យ​កម្មវិធី​នាំ​ផ្លូវ​លទ្ធផល​មáŸážŒáŸ€â€‹áž‘ៅ​ឧបករណáŸâ€‹â€‹ážáž¶áž„​ក្រៅ​ផ្សáŸáž„។"</string> - <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណáŸâ€‹áž•្ទុក​សុវážáŸ’ážáž·áž—ាព​"</string> + <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណáŸâ€‹áž•្ទុក​សុវážáŸ’ážáž·áž—áž¶áž–"</string> <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យ​កម្មវិធី​ចូល​​ការ​ផ្ទុក​មាន​សុវážáŸ’ážáž·áž—áž¶áž– keguard ។"</string> <string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិážáŸ’យ​ការ​បង្ហាញ និង​លាក់​ការ​ការពារ"</string> <string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យ​កម្មវិធី​គ្រប់គ្រង keguard ។"</string> @@ -1365,7 +1365,7 @@ <string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string> <string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string> <string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string> - <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់​"</string> + <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string> <string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string> <string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string> <string name="ime_action_default" msgid="2840921885558045721">"អនុវážáŸ’áž"</string> @@ -1374,7 +1374,7 @@ <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធី​មួយ ឬ​ច្រើន​ដូច​ážáž¶áž„​ក្រោម​ស្នើ​សិទ្ធិ ដើម្បី​ចូល​គណនី​របស់​អ្នក​ឥឡូវ និង​ពáŸáž›â€‹áž¢áž“áž¶áž‚ážáŸ”"</string> <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"ážáž¾â€‹áž¢áŸ’នក​ចង់​អនុញ្ញាážâ€‹ážŸáŸ†ážŽáž¾â€‹áž“áŸáŸ‡?"</string> <string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើ​ចូល"</string> - <string name="allow" msgid="7225948811296386551">"អនុញ្ញាážâ€‹"</string> + <string name="allow" msgid="7225948811296386551">"អនុញ្ញាáž"</string> <string name="deny" msgid="2081879885755434506">"បដិសáŸáž’"</string> <string name="permission_request_notification_title" msgid="6486759795926237907">"បាន​ស្នើ​សិទ្ធិ"</string> <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បាន​ស្នើ​សិទ្ធិ\nសម្រាប់​គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string> @@ -1397,12 +1397,12 @@ <string name="no_file_chosen" msgid="6363648562170759465">"គ្មាន​ឯកសារ​បាន​ជ្រើស"</string> <string name="reset" msgid="2448168080964209908">"កំណážáŸ‹â€‹áž¡áž¾áž„វិញ"</string> <string name="submit" msgid="1602335572089911941">"ដាក់​ស្នើ"</string> - <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រážáž™áž“្ážâ€‹"</string> + <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រážáž™áž“្áž"</string> <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ​ ដើម្បី​ចáŸáž‰â€‹áž–ី​របៀប​រážáž™áž“្ážâ€‹áŸ”"</string> <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬ​ហážážŸáŸ’áž–ážâ€‹ážŸáž€áž˜áŸ’ម"</string> <string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ​ ដើម្បី​រៀបចំ។"</string> <string name="back_button_label" msgid="2300470004503343439">"ážáž™áž€áŸ’រោយ"</string> - <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់​"</string> + <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string> <string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string> <string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការ​ប្រើ​ទិន្ននáŸáž™â€‹áž…áž›áŸážâ€‹ážáŸ’ពស់"</string> <string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ​ ដើម្បី​​ស្វែងយល់​បន្ážáŸ‚ម​អំពី​ការ​ប្រើ​​​ទិន្ននáŸáž™â€‹áž…áž›áŸážâ€‹áŸ”"</string> @@ -1428,7 +1428,7 @@ <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណáŸâ€‹áž•្ទុក​យូអáŸážŸáž”៊ី​បច្ចុប្បន្ន​កំពុង​ប្រើ​ដោយ​កុំព្យូទáŸážšáŸ”"</string> <string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្ន​កាážâ€‹áž¢áŸážŸážŒáž¸â€‹áž€áŸ†áž–ុង​ប្រើ​ដោយ​កុំព្យូទáŸážš"</string> <string name="media_unknown_state" msgid="729192782197290385">"មិន​ស្គាល់​ស្ážáž¶áž“ភាព​មáŸážŒáŸ€â€‹ážáž¶áž„​ក្រៅ។"</string> - <string name="share" msgid="1778686618230011964">"ចែក​រំលែក​"</string> + <string name="share" msgid="1778686618230011964">"ចែក​រំលែក"</string> <string name="find" msgid="4808270900322985960">"រក"</string> <string name="websearch" msgid="4337157977400211589">"ស្វែងរក​ážáž¶áž˜â€‹áž”ណ្ដាញ"</string> <string name="find_next" msgid="5742124618942193978">"រក​បន្ទាប់"</string> @@ -1444,7 +1444,7 @@ <string name="sync_undo_deletes" msgid="2941317360600338602">"មិន​ធ្វើ​ការ​លុប​វិញ"</string> <string name="sync_do_nothing" msgid="3743764740430821845">"មិន​ធ្វើអ្វី​ទáŸâ€‹áž¥áž¡áž¼ážœ"</string> <string name="choose_account_label" msgid="5655203089746423927">"ជ្រើស​គណនី"</string> - <string name="add_account_label" msgid="2935267344849993553">"បន្ážáŸ‚ម​គណនី​ážáŸ’មី​​"</string> + <string name="add_account_label" msgid="2935267344849993553">"បន្ážáŸ‚ម​គណនី​ážáŸ’មី"</string> <string name="add_account_button_label" msgid="3611982894853435874">"បន្ážáŸ‚ម​គណនី"</string> <string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string> <string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ážáž™"</string> @@ -1463,15 +1463,15 @@ <string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើន​​ឆ្នាំ"</string> <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ážáž™â€‹áž†áŸ’នាំ"</string> <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string> - <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string> + <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string> <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string> <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string> <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string> <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string> <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string> - <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី​​"</string> + <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី"</string> <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ​"</string> + <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ"</string> <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែក​រំលែក​ជា​មួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រង​ការ​រុញ។ ប៉ះ & សង្កážáŸ‹áŸ”"</string> <string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស​ ដើម្បី​ដោះ​សោ។"</string> @@ -1485,7 +1485,7 @@ <string name="storage_internal" msgid="4891916833657929263">"ឧបករណáŸâ€‹áž•្ទុក​ážáž¶áž„​ក្នុង"</string> <string name="storage_sd_card" msgid="3282948861378286745">"កាážâ€‹áž¢áŸážŸážŒáž¸"</string> <string name="storage_usb" msgid="3017954059538517278">"ឧបករណáŸâ€‹áž•្ទុក​យូអáŸážŸáž”៊ី"</string> - <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល​"</string> + <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string> <string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមាន​ប្រើ​ទិន្ននáŸáž™"</string> <string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បី​មើល​ការ​ប្រើ និង​ការ​កំណážáŸ‹áŸ”"</string> <string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បាន​បិទ​ទិន្ននáŸáž™ 2G​-3G"</string> @@ -1542,7 +1542,7 @@ <string name="media_route_status_available" msgid="6983258067194649391">"ទំនáŸážš"</string> <string name="media_route_status_not_available" msgid="6739899962681886401">"មិន​ទំនáŸážš"</string> <string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុង​ប្រើ"</string> - <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អáŸáž€áŸ’រង់​ជាប់​"</string> + <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អáŸáž€áŸ’រង់​ជាប់"</string> <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អáŸáž€áŸ’រង់ HDMI"</string> <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ážáŸ’ážšáž½ážâ€‹áž‚្នា"</string> <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string> @@ -1574,7 +1574,7 @@ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពáŸáž€"</string> <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string> <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string> - <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាážáŸ‹â€‹"</string> + <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាážáŸ‹"</string> <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាážáŸ‹â€‹áž˜áž·áž“​ážáŸ’រឹមážáŸ’រូវ។"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លáŸáž…​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាážáŸ‹â€‹ážšáž”ស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string> @@ -1683,7 +1683,7 @@ <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string> <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"​មិន​ស្គាល់​បញ្ឈរ"</string> <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិន​ស្គាល់​ទáŸážŸáž—áž¶áž–"</string> - <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់​"</string> + <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់"</string> <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុស​ក្នុង​ការ​សរសáŸážšâ€‹áž˜áž¶ážáž·áž€áž¶"</string> <string name="reason_unknown" msgid="6048913880184628119">"មិន​ស្គាល់"</string> <string name="reason_service_unavailable" msgid="7824008732243903268">"មិន​បា​ន​បើក​សáŸážœáž¶áž€áž˜áŸ’ម​បោះពុម្ព"</string> diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml index 3b275a5..71f55fb 100644 --- a/core/res/res/values-lo-rLA/strings.xml +++ b/core/res/res/values-lo-rLA/strings.xml @@ -1695,7 +1695,7 @@ <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະ​ຈຸ​ບັນ"</string> <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃà»à»ˆ"</string> <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃà»à»ˆ"</string> - <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສà»àº²â€‹àº¥àº±àºšâ€‹àºàº²àº™â€‹àº›àº±àºšâ€‹àº›àº¸àº‡â€‹àº‚à»à»‰àºˆà»àº²â€‹àºàº±àº”​"</string> + <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສà»àº²â€‹àº¥àº±àºšâ€‹àºàº²àº™â€‹àº›àº±àºšâ€‹àº›àº¸àº‡â€‹àº‚à»à»‰àºˆà»àº²â€‹àºàº±àº”"</string> <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບà»à»ˆâ€‹àºàº»àº‡àºàº±àº™. ລàºàº‡à»ƒà»à»ˆàºàºµàºàº„ັ້ງ​."</string> <string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ​ສັ້ນ​ເàºàºµàº™â€‹à»„ປ​. ຕ້àºàº‡àº¡àºµàº¢à»ˆàº²àº‡à»œà»‰àºàº 4 ຫຼັàºâ€‹."</string> <plurals name="restr_pin_countdown"> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 4640f2a..d235eaa 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -187,7 +187,7 @@ <string name="global_action_settings" msgid="1756531602592545966">"à¸à¸²à¸£à¸•ั้งค่า"</string> <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string> <string name="safeMode" msgid="2788228061547930246">"โหมดปลà¸à¸”ภัย"</string> - <string name="android_system_label" msgid="6577375335728551336">"ระบบ Android"</string> + <string name="android_system_label" msgid="6577375335728551336">"ระบบà¹à¸à¸™à¸”รà¸à¸¢à¸”์"</string> <string name="user_owner_label" msgid="2804351898001038951">"ส่วนตัว"</string> <string name="managed_profile_label" msgid="6260850669674791528">"ที่ทำงาน"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"บริà¸à¸²à¸£à¸—ี่ต้à¸à¸‡à¹€à¸ªà¸µà¸¢à¸„่าใช้จ่าย"</string> @@ -424,7 +424,7 @@ <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันทำให้ส่วนหนึ่งขà¸à¸‡à¸•ัวเà¸à¸‡à¸„งà¸à¸¢à¸¹à¹ˆà¸–าวรในหน่วยความจำ ซึ่งจะจำà¸à¸±à¸”พื้นที่หน่วยความจำที่ใช้งานได้ขà¸à¸‡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันà¸à¸·à¹ˆà¸™à¹† à¹à¸¥à¸°à¸—ำให้à¹à¸—็บเล็ตทำงานช้าลง"</string> <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันทำให้ส่วนหนึ่งขà¸à¸‡à¸•ัวเà¸à¸‡à¸„งà¸à¸¢à¸¹à¹ˆà¸–าวรในหน่วยความจำ ซึ่งจะจำà¸à¸±à¸”พื้นที่หน่วยความจำที่ใช้งานได้ขà¸à¸‡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันà¸à¸·à¹ˆà¸™à¹† à¹à¸¥à¸°à¸—ำให้โทรศัพท์ทำงานช้าลง"</string> <string name="permlab_deletePackages" msgid="184385129537705938">"ลบà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน"</string> - <string name="permdesc_deletePackages" msgid="7411480275167205081">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันลบà¹à¸žà¹‡à¸à¹€à¸à¸ˆ Android à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันที่เป็นà¸à¸±à¸™à¸•รายà¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•นี้ลบà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันที่สำคัà¸"</string> + <string name="permdesc_deletePackages" msgid="7411480275167205081">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันลบà¹à¸žà¹‡à¸à¹€à¸à¸ˆà¹à¸à¸™à¸”รà¸à¸¢à¸”์à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันที่เป็นà¸à¸±à¸™à¸•รายà¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•นี้ลบà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันที่สำคัà¸"</string> <string name="permlab_clearAppUserData" msgid="274109191845842756">"ลบข้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันà¸à¸·à¹ˆà¸™"</string> <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันล้างข้à¸à¸¡à¸¹à¸¥à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰"</string> <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"ลบà¹à¸„ชขà¸à¸‡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันà¸à¸·à¹ˆà¸™"</string> @@ -432,7 +432,7 @@ <string name="permlab_getPackageSize" msgid="7472921768357981986">"วัดพื้นที่เà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน"</string> <string name="permdesc_getPackageSize" msgid="3921068154420738296">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันเรียà¸à¸”ูรหัส ข้à¸à¸¡à¸¹à¸¥ à¹à¸¥à¸°à¸‚นาดà¹à¸„ชขà¸à¸‡à¸•น"</string> <string name="permlab_installPackages" msgid="2199128482820306924">"ติดตั้งà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันโดยตรง"</string> - <string name="permdesc_installPackages" msgid="5628530972548071284">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันติดตั้งà¹à¸žà¹‡à¸à¹€à¸à¸ˆ Android ใหม่หรืà¸à¸—ี่à¸à¸±à¸›à¹€à¸”ต à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันที่เป็นà¸à¸±à¸™à¸•รายà¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•นี้ในà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันใหม่ๆ ด้วยสิทธิ์ที่สูงนี้ได้ตามต้à¸à¸‡à¸à¸²à¸£"</string> + <string name="permdesc_installPackages" msgid="5628530972548071284">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันติดตั้งà¹à¸žà¹‡à¸à¹€à¸à¸ˆà¹à¸à¸™à¸”รà¸à¸¢à¸”์ใหม่หรืà¸à¸—ี่à¸à¸±à¸›à¹€à¸”ต à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันที่เป็นà¸à¸±à¸™à¸•รายà¸à¸²à¸ˆà¹ƒà¸Šà¹‰à¸à¸²à¸£à¸à¸™à¸¸à¸à¸²à¸•นี้ในà¸à¸²à¸£à¹€à¸žà¸´à¹ˆà¸¡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันใหม่ๆ ด้วยสิทธิ์ที่สูงนี้ได้ตามต้à¸à¸‡à¸à¸²à¸£"</string> <string name="permlab_clearAppCache" msgid="7487279391723526815">"ลบข้à¸à¸¡à¸¹à¸¥à¹à¸„ชขà¸à¸‡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันทั้งหมด"</string> <string name="permdesc_clearAppCache" product="tablet" msgid="8974640871945434565">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันสร้างพื้นที่ว่างในที่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¹à¸—็บเล็ต โดยลบไฟล์ในไดเรà¸à¸—à¸à¸£à¸µà¹à¸„ชขà¸à¸‡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันà¸à¸·à¹ˆà¸™à¹† ซึ่งà¸à¸²à¸ˆà¸—ำให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันà¸à¸·à¹ˆà¸™à¹€à¸£à¸´à¹ˆà¸¡à¸—ำงานช้าà¸à¸§à¹ˆà¸²à¹€à¸”ิมเนื่à¸à¸‡à¸ˆà¸²à¸à¸•้à¸à¸‡à¸”ึงข้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¸•นซ้ำ"</string> <string name="permdesc_clearAppCache" product="default" msgid="2459441021956436779">"à¸à¸™à¸¸à¸à¸²à¸•ให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันสร้างพื้นที่ว่างในที่จัดเà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¹‚ทรศัพท์ โดยลบไฟล์ในไดเรà¸à¸—à¸à¸£à¸µà¹à¸„ชขà¸à¸‡à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันà¸à¸·à¹ˆà¸™à¹† ซึ่งà¸à¸²à¸ˆà¸—ำให้à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชันà¸à¸·à¹ˆà¸™à¹€à¸£à¸´à¹ˆà¸¡à¸—ำงานช้าà¸à¸§à¹ˆà¸²à¹€à¸”ิมเนื่à¸à¸‡à¸ˆà¸²à¸à¸•้à¸à¸‡à¸”ึงข้à¸à¸¡à¸¹à¸¥à¸‚à¸à¸‡à¸•นซ้ำ"</string> @@ -1190,7 +1190,7 @@ <string name="screen_compat_mode_hint" msgid="1064524084543304459">"เปิดใช้งานà¸à¸µà¸à¸„รั้งในà¸à¸²à¸£à¸•ั้งค่าระบบ > à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน > ดาวน์โหลด"</string> <string name="smv_application" msgid="3307209192155442829">"à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน <xliff:g id="APPLICATION">%1$s</xliff:g> (à¸à¸£à¸°à¸šà¸§à¸™à¸à¸²à¸£ <xliff:g id="PROCESS">%2$s</xliff:g>) ละเมิดนโยบาย StrictMode ที่บังคับใช้ด้วยตัวเà¸à¸‡"</string> <string name="smv_process" msgid="5120397012047462446">"à¸à¸£à¸°à¸šà¸§à¸™à¸à¸²à¸£ <xliff:g id="PROCESS">%1$s</xliff:g> ละเมิดนโยบาย StrictMode ที่บังคับใช้ด้วยตัวเà¸à¸‡"</string> - <string name="android_upgrading_title" msgid="1584192285441405746">"à¸à¸³à¸¥à¸±à¸‡à¸à¸±à¸›à¹€à¸à¸£à¸” Android..."</string> + <string name="android_upgrading_title" msgid="1584192285441405746">"à¸à¸³à¸¥à¸±à¸‡à¸à¸±à¸›à¹€à¸à¸£à¸”à¹à¸à¸™à¸”รà¸à¸¢à¸”์..."</string> <string name="android_upgrading_apk" msgid="7904042682111526169">"à¸à¸³à¸¥à¸±à¸‡à¹€à¸žà¸´à¹ˆà¸¡à¸›à¸£à¸°à¸ªà¸´à¸—ธิภาพà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน <xliff:g id="NUMBER_0">%1$d</xliff:g> จาภ<xliff:g id="NUMBER_1">%2$d</xliff:g> รายà¸à¸²à¸£"</string> <string name="android_upgrading_starting_apps" msgid="451464516346926713">"à¸à¸³à¸¥à¸±à¸‡à¹€à¸£à¸´à¹ˆà¸¡à¸•้นà¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน"</string> <string name="android_upgrading_complete" msgid="1405954754112999229">"เสร็จสิ้นà¸à¸²à¸£à¸šà¸¹à¸•"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index c0286f1..f1f1802 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4065,6 +4065,9 @@ The default is one. See {@link android.widget.GridLayout.Spec}. --> <attr name="layout_rowSpan" format="integer" min="1" /> + <!-- The relative proportion of horizontal space that should be allocated to this view + during excess space distribution. --> + <attr name="layout_rowWeight" format="float" /> <!-- The column boundary delimiting the left of the group of cells occupied by this view. --> <attr name="layout_column" /> @@ -4073,6 +4076,9 @@ The default is one. See {@link android.widget.GridLayout.Spec}. --> <attr name="layout_columnSpan" format="integer" min="1" /> + <!-- The relative proportion of vertical space that should be allocated to this view + during excess space distribution. --> + <attr name="layout_columnWeight" format="float" /> <!-- Gravity specifies how a component should be placed in its group of cells. The default is LEFT | BASELINE. See {@link android.widget.GridLayout.LayoutParams#setGravity(int)}. --> @@ -4782,6 +4788,28 @@ <attr name="height" /> </declare-styleable> + <!-- Defines the group used in Vector Drawables. --> + <declare-styleable name="VectorDrawableGroup"> + <!-- The Name of this group --> + <attr name="name" /> + <!-- The amount to rotate the group --> + <attr name="rotation" /> + <!-- The X coordinate of the center of rotation of a group --> + <attr name="pivotX" /> + <!-- The Y coordinate of the center of rotation of a group --> + <attr name="pivotY" /> + <!-- The amount to translate the group on X coordinate --> + <attr name="translateX" format="float"/> + <!-- The amount to translate the group on Y coordinate --> + <attr name="translateY" format="float"/> + <!-- The amount to scale the group on X coordinate --> + <attr name="scaleX" /> + <!-- The amount to scale the group on X coordinate --> + <attr name="scaleY" /> + <!-- The alpha of the group (0 is transparent and 1 is opaque) --> + <attr name="alpha" /> + </declare-styleable> + <!-- Defines the path used in Vector Drawables. --> <declare-styleable name="VectorDrawablePath"> <!-- The Name of this path --> @@ -4790,12 +4818,6 @@ <attr name="strokeWidth" format="float" /> <!-- The opacity of a path stroke --> <attr name="strokeOpacity" format="float" /> - <!-- The amount to rotate the path stroke --> - <attr name="rotation" /> - <!-- The X coordinate of the center of rotation of a path --> - <attr name="pivotX" /> - <!-- The Y coordinate of the center of rotation of a path --> - <attr name="pivotY" /> <!-- The color to stroke the path if not defined implies no stroke--> <attr name="stroke" format="color" /> <!-- The color to fill the path if not defined implies no fill--> @@ -6127,6 +6149,11 @@ <!-- Component name of an activity that allows the user to modify the settings for this trust agent. --> <attr name="settingsActivity" /> + <!-- Title for a preference that allows that user to launch the + activity to modify trust agent settings. --> + <attr name="title" /> + <!-- Summary for the same preference as the title. --> + <attr name="summary" /> </declare-styleable> <!-- =============================== --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e9a2f9f..7fc2f49 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2178,6 +2178,10 @@ <public type="attr" name="contentInsetLeft" /> <public type="attr" name="contentInsetRight" /> <public type="attr" name="paddingMode" /> + <public type="attr" name="layout_rowWeight" /> + <public type="attr" name="layout_columnWeight" /> + <public type="attr" name="translateX" /> + <public type="attr" name="translateY" /> <public type="attr" name="selectableItemBackgroundBorderless" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 7234911..9ff67b4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -457,10 +457,10 @@ <string name="android_system_label">Android System</string> <!-- Label for the user owner in the intent forwarding app. --> - <string name="user_owner_label">Personal</string> + <string name="user_owner_label">Personal apps</string> <!-- Label for a corporate profile in the intent forwarding app. --> - <string name="managed_profile_label">Work</string> + <string name="managed_profile_label">Android for Work</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_costMoney">Services that cost you money</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index a0b3b63..59bd667 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -237,14 +237,6 @@ please see styles_device_defaults.xml. <item name="windowExitAnimation">@anim/fast_fade_out</item> </style> - <!-- Window animations for swipe-dismissable windows. {@hide} --> - <style name="Animation.SwipeDismiss"> - <item name="taskOpenEnterAnimation">@anim/swipe_window_enter</item> - <item name="taskOpenExitAnimation">@anim/swipe_window_exit</item> - <item name="taskCloseEnterAnimation">@anim/swipe_window_enter</item> - <item name="taskCloseExitAnimation">@anim/swipe_window_exit</item> - </style> - <!-- Status Bar Styles --> <style name="TextAppearance.StatusBar"> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml index 5bac1f9..0c854d3 100644 --- a/core/res/res/values/styles_micro.xml +++ b/core/res/res/values/styles_micro.xml @@ -14,6 +14,19 @@ limitations under the License. --> <resources> + <style name="Animation.Micro"/> + + <style name="Animation.Micro.Activity" parent="Animation.Holo.Activity"> + <item name="activityOpenEnterAnimation">@anim/slide_in_micro</item> + <item name="activityOpenExitAnimation">@null</item> + <item name="activityCloseEnterAnimation">@null</item> + <item name="activityCloseExitAnimation">@anim/slide_out_micro</item> + <item name="taskOpenEnterAnimation">@anim/slide_in_micro</item> + <item name="taskOpenExitAnimation">@null</item> + <item name="taskCloseEnterAnimation">@null</item> + <item name="taskCloseExitAnimation">@anim/slide_out_micro</item> + </style> + <style name="AlertDialog.Micro" parent="AlertDialog.Holo.Light"> <item name="fullDark">@null</item> <item name="topDark">@null</item> diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml index ebdab5b..7e0467b 100644 --- a/core/res/res/values/themes_micro.xml +++ b/core/res/res/values/themes_micro.xml @@ -19,15 +19,14 @@ <item name="alertDialogStyle">@style/AlertDialog.Micro</item> <item name="dialogTheme">@style/Theme.Micro.Dialog</item> <item name="textViewStyle">@style/Widget.Micro.TextView</item> - <item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item> - <item name="windowAnimationStyle">@style/Animation.SwipeDismiss</item> + <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item> <item name="windowBackground">@color/black</item> <item name="windowContentOverlay">@null</item> <item name="windowIsFloating">false</item> <item name="windowIsTranslucent">true</item> <item name="windowSwipeToDismiss">true</item> - </style> + </style> <style name="Theme.Micro.Light" parent="Theme.Holo.Light.NoActionBar"> <item name="alertDialogTheme">@style/Theme.Micro.Dialog.Alert</item> @@ -35,7 +34,7 @@ <item name="dialogTheme">@style/Theme.Micro.Dialog</item> <item name="textViewStyle">@style/Widget.Micro.TextView</item> <item name="numberPickerStyle">@style/Widget.Micro.NumberPicker</item> - <item name="windowAnimationStyle">@style/Animation.SwipeDismiss</item> + <item name="windowAnimationStyle">@style/Animation.Micro.Activity</item> <item name="windowBackground">@color/white</item> <item name="windowContentOverlay">@null</item> <item name="windowIsFloating">false</item> diff --git a/core/tests/coretests/src/android/os/FileBridgeTest.java b/core/tests/coretests/src/android/os/FileBridgeTest.java new file mode 100644 index 0000000..d4f6b1f --- /dev/null +++ b/core/tests/coretests/src/android/os/FileBridgeTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.FileBridge.FileBridgeOutputStream; +import android.test.AndroidTestCase; +import android.test.MoreAsserts; + +import libcore.io.Streams; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Random; + +public class FileBridgeTest extends AndroidTestCase { + + private File file; + private FileOutputStream fileOs; + private FileBridge bridge; + private FileBridgeOutputStream client; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + file = getContext().getFileStreamPath("meow.dat"); + file.delete(); + + fileOs = new FileOutputStream(file); + + bridge = new FileBridge(); + bridge.setTargetFile(fileOs.getFD()); + bridge.start(); + client = new FileBridgeOutputStream(bridge.getClientSocket()); + } + + @Override + protected void tearDown() throws Exception { + fileOs.close(); + file.delete(); + } + + private void assertOpen() throws Exception { + assertFalse("expected open", bridge.isClosed()); + } + + private void closeAndAssertClosed() throws Exception { + client.close(); + + // Wait a beat for things to settle down + SystemClock.sleep(200); + assertTrue("expected closed", bridge.isClosed()); + } + + private void assertContents(byte[] expected) throws Exception { + MoreAsserts.assertEquals(expected, Streams.readFully(new FileInputStream(file))); + } + + public void testNoWriteNoSync() throws Exception { + assertOpen(); + closeAndAssertClosed(); + } + + public void testNoWriteSync() throws Exception { + assertOpen(); + client.flush(); + closeAndAssertClosed(); + } + + public void testWriteNoSync() throws Exception { + assertOpen(); + client.write("meow".getBytes(StandardCharsets.UTF_8)); + closeAndAssertClosed(); + assertContents("meow".getBytes(StandardCharsets.UTF_8)); + } + + public void testWriteSync() throws Exception { + assertOpen(); + client.write("cake".getBytes(StandardCharsets.UTF_8)); + client.flush(); + closeAndAssertClosed(); + assertContents("cake".getBytes(StandardCharsets.UTF_8)); + } + + public void testWriteSyncWrite() throws Exception { + assertOpen(); + client.write("meow".getBytes(StandardCharsets.UTF_8)); + client.flush(); + client.write("cake".getBytes(StandardCharsets.UTF_8)); + closeAndAssertClosed(); + assertContents("meowcake".getBytes(StandardCharsets.UTF_8)); + } + + public void testEmptyWrite() throws Exception { + assertOpen(); + client.write(new byte[0]); + closeAndAssertClosed(); + assertContents(new byte[0]); + } + + public void testWriteAfterClose() throws Exception { + assertOpen(); + client.write("meow".getBytes(StandardCharsets.UTF_8)); + closeAndAssertClosed(); + try { + client.write("cake".getBytes(StandardCharsets.UTF_8)); + fail("wrote after close!"); + } catch (IOException expected) { + } + assertContents("meow".getBytes(StandardCharsets.UTF_8)); + } + + public void testRandomWrite() throws Exception { + final Random r = new Random(); + final ByteArrayOutputStream result = new ByteArrayOutputStream(); + + for (int i = 0; i < 512; i++) { + final byte[] test = new byte[r.nextInt(24169)]; + r.nextBytes(test); + result.write(test); + client.write(test); + client.flush(); + } + + closeAndAssertClosed(); + assertContents(result.toByteArray()); + } + + public void testGiantWrite() throws Exception { + final byte[] test = new byte[263401]; + new Random().nextBytes(test); + + assertOpen(); + client.write(test); + closeAndAssertClosed(); + assertContents(test); + } +} diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java index 5dc9ef8..433d4d2 100644 --- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java @@ -16,6 +16,9 @@ package com.android.internal.util; +import android.test.MoreAsserts; + +import java.util.Arrays; import junit.framework.TestCase; /** @@ -77,4 +80,79 @@ public class ArrayUtilsTest extends TestCase { assertFalse(ArrayUtils.containsAll(new Object[] { }, new Object[] { null })); assertFalse(ArrayUtils.containsAll(new Object[] { A }, new Object[] { null })); } + + public void testContainsInt() throws Exception { + assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 1)); + assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 2)); + assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 3)); + + assertFalse(ArrayUtils.contains(new int[] { 1, 2, 3 }, 0)); + assertFalse(ArrayUtils.contains(new int[] { 1, 2, 3 }, 4)); + assertFalse(ArrayUtils.contains(new int[] { }, 2)); + } + + public void testAppendInt() throws Exception { + MoreAsserts.assertEquals(new int[] { 1 }, + ArrayUtils.appendInt(null, 1)); + MoreAsserts.assertEquals(new int[] { 1 }, + ArrayUtils.appendInt(new int[] { }, 1)); + MoreAsserts.assertEquals(new int[] { 1, 2 }, + ArrayUtils.appendInt(new int[] { 1 }, 2)); + MoreAsserts.assertEquals(new int[] { 1, 2 }, + ArrayUtils.appendInt(new int[] { 1, 2 }, 1)); + } + + public void testRemoveInt() throws Exception { + assertNull(ArrayUtils.removeInt(null, 1)); + MoreAsserts.assertEquals(new int[] { }, + ArrayUtils.removeInt(new int[] { }, 1)); + MoreAsserts.assertEquals(new int[] { 1, 2, 3, }, + ArrayUtils.removeInt(new int[] { 1, 2, 3}, 4)); + MoreAsserts.assertEquals(new int[] { 2, 3, }, + ArrayUtils.removeInt(new int[] { 1, 2, 3}, 1)); + MoreAsserts.assertEquals(new int[] { 1, 3, }, + ArrayUtils.removeInt(new int[] { 1, 2, 3}, 2)); + MoreAsserts.assertEquals(new int[] { 1, 2, }, + ArrayUtils.removeInt(new int[] { 1, 2, 3}, 3)); + MoreAsserts.assertEquals(new int[] { 2, 3, 1 }, + ArrayUtils.removeInt(new int[] { 1, 2, 3, 1 }, 1)); + } + + public void testContainsLong() throws Exception { + assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 1)); + assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 2)); + assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 3)); + + assertFalse(ArrayUtils.contains(new long[] { 1, 2, 3 }, 0)); + assertFalse(ArrayUtils.contains(new long[] { 1, 2, 3 }, 4)); + assertFalse(ArrayUtils.contains(new long[] { }, 2)); + } + + public void testAppendLong() throws Exception { + MoreAsserts.assertEquals(new long[] { 1 }, + ArrayUtils.appendLong(null, 1)); + MoreAsserts.assertEquals(new long[] { 1 }, + ArrayUtils.appendLong(new long[] { }, 1)); + MoreAsserts.assertEquals(new long[] { 1, 2 }, + ArrayUtils.appendLong(new long[] { 1 }, 2)); + MoreAsserts.assertEquals(new long[] { 1, 2 }, + ArrayUtils.appendLong(new long[] { 1, 2 }, 1)); + } + + public void testRemoveLong() throws Exception { + assertNull(ArrayUtils.removeLong(null, 1)); + MoreAsserts.assertEquals(new long[] { }, + ArrayUtils.removeLong(new long[] { }, 1)); + MoreAsserts.assertEquals(new long[] { 1, 2, 3, }, + ArrayUtils.removeLong(new long[] { 1, 2, 3}, 4)); + MoreAsserts.assertEquals(new long[] { 2, 3, }, + ArrayUtils.removeLong(new long[] { 1, 2, 3}, 1)); + MoreAsserts.assertEquals(new long[] { 1, 3, }, + ArrayUtils.removeLong(new long[] { 1, 2, 3}, 2)); + MoreAsserts.assertEquals(new long[] { 1, 2, }, + ArrayUtils.removeLong(new long[] { 1, 2, 3}, 3)); + MoreAsserts.assertEquals(new long[] { 2, 3, 1 }, + ArrayUtils.removeLong(new long[] { 1, 2, 3, 1 }, 1)); + } + } diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk new file mode 100644 index 0000000..d649154 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk @@ -0,0 +1,42 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) + + +## The application with a minimal main dex +include $(CLEAR_VARS) + +LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_SDK_VERSION := current + +LOCAL_PACKAGE_NAME := MultiDexLegacyAndException + +mainDexList:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex + +include $(BUILD_PACKAGE) + +$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses + $(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@ + echo "com/android/multidexlegacyandexception/Test.class" >> $@ + +$(built_dex_intermediate): $(mainDexList) + diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml new file mode 100644 index 0000000..7fff711 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.multidexlegacyandexception" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/> + + <application + android:name="com.android.multidexlegacyandexception.TestApplication" + android:label="multidexlegacyandexception" + > + <activity + android:name="com.android.multidexlegacyandexception.MainActivity" + android:label="multidexlegacyandexception" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.multidexlegacyandexception" + android:label="Test for MultiDexLegacyAndException" /> +</manifest> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml new file mode 100644 index 0000000..37eb613 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/layout/activity_main.xml @@ -0,0 +1,13 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + tools:context=".MainActivity" > + + <TextView + android:id="@+id/label_nb" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_nb" /> + +</RelativeLayout> diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml new file mode 100644 index 0000000..e56e049 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/res/values/strings.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">MultidexLegacyAndException</string> + <string name="action_settings">Settings</string> + <string name="label_nb">Here\'s the count: </string> + +</resources> diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java index 4c276b7..d6883ec 100644 --- a/core/java/com/android/internal/backup/BackupConstants.java +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,8 @@ * limitations under the License. */ -package com.android.internal.backup; +package com.android.multidexlegacyandexception; + +public class CaughtOnlyByIntermediateException extends RuntimeException { -/** - * Constants used internally between the backup manager and its transports - */ -public class BackupConstants { - public static final int TRANSPORT_OK = 0; - public static final int TRANSPORT_ERROR = 1; - public static final int TRANSPORT_NOT_INITIALIZED = 2; - public static final int AGENT_ERROR = 3; - public static final int AGENT_UNKNOWN = 4; } diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java new file mode 100644 index 0000000..4903e01 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyException.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class CaughtOnlyException extends RuntimeException { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java new file mode 100644 index 0000000..b08a11a --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ClassInSecondaryDex.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ClassInSecondaryDex { + private boolean condition; + + public ClassInSecondaryDex(boolean condition) { + this.condition = condition; + } + + public void canThrow1() throws ExceptionInMainDex, ExceptionInMainDex2, + ExceptionInSecondaryDexWithSuperInMain { + if (condition) { + throw new ExceptionInMainDex(); + } + } + + public void canThrow2() throws ExceptionInSecondaryDex, ExceptionInSecondaryDex2, + ExceptionInSecondaryDexWithSuperInMain { + if (condition) { + throw new ExceptionInSecondaryDex(); + } + } + + public static void canThrowAll(Throwable toThrow) throws Throwable { + if (toThrow != null) { + throw toThrow; + } + } + + public int get1() { + try { + canThrow1(); + canThrow2(); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (OutOfMemoryError e) { + return 12; + } catch (CaughtOnlyException e) { + return 17; + } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex e) { + return 23; + } + } + + public int get2() { + try { + canThrow2(); + canThrow1(); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (OutOfMemoryError e) { + return 12; + } catch (CaughtOnlyException e) { + return 17; + } catch (SuperExceptionInSecondaryDex e) { + return 23; + } catch (SuperExceptionInMainDex e) { + return 27; + } + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java new file mode 100644 index 0000000..7fc3d73 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInMainDex extends SuperExceptionInMainDex { +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java new file mode 100644 index 0000000..3fbeac6 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInMainDex2.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInMainDex2 extends SuperExceptionInMainDex { +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java new file mode 100644 index 0000000..9401c05 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInSecondaryDex extends SuperExceptionInSecondaryDex { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java new file mode 100644 index 0000000..d1aa103 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDex2.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInSecondaryDex2 extends SuperExceptionInSecondaryDex { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java new file mode 100644 index 0000000..9327882 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/ExceptionInSecondaryDexWithSuperInMain.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class ExceptionInSecondaryDexWithSuperInMain extends SuperExceptionInMainDex { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java new file mode 100644 index 0000000..dfdc4af --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/IntermediateClass.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.multidexlegacyandexception; + +public class IntermediateClass { + + public static int get1(boolean condition) { + return new ClassInSecondaryDex(condition).get1(); + } + + public static int get2(boolean condition) { + return new ClassInSecondaryDex(condition).get2(); + } + + public static int get3(boolean condition) { + ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition); + try { + thrower.canThrow2(); + thrower.canThrow1(); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (ExceptionInMainDex2 e) { + return 10; + } catch (ExceptionInSecondaryDex2 e) { + return 11; + } catch (OutOfMemoryError e) { + return 12; + } catch (CaughtOnlyException e) { + return 17; + } catch (ExceptionInSecondaryDexWithSuperInMain e) { + return 39; + } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex|CaughtOnlyByIntermediateException e) { + return 23; + } + } + + public static int get4(boolean condition) { + ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition); + try { + thrower.canThrow2(); + thrower.canThrow1(); + return 1; + } catch (ExceptionInSecondaryDexWithSuperInMain e) { + return 39; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (ExceptionInSecondaryDex2 e) { + return 11; + } catch (OutOfMemoryError e) { + return 12; + } catch (ExceptionInMainDex2 e) { + return 10; + } catch (ExceptionInMainDex e) { + return 10; + } catch (CaughtOnlyException e) { + return 17; + } catch (SuperExceptionInSecondaryDex e) { + } catch (SuperExceptionInMainDex e) { + } catch (CaughtOnlyByIntermediateException e) { + return 35; + } + return 39; + } + + + public static int get5(Throwable thrown) { + try { + ClassInSecondaryDex.canThrowAll(thrown); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (ExceptionInMainDex2 e) { + return 12; + } catch (ExceptionInSecondaryDex2 e) { + return 13; + } catch (OutOfMemoryError e) { + return 14; + } catch (CaughtOnlyException e) { + return 17; + } catch (ExceptionInSecondaryDexWithSuperInMain e) { + return 39; + } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex|CaughtOnlyByIntermediateException e) { + return 23; + } catch (Throwable e) { + return 37; + } + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java new file mode 100644 index 0000000..dd2ce7a --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MainActivity.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.multidexlegacyandexception; + +import android.app.Activity; +import android.os.Bundle; + +public class MainActivity extends Activity { + + public MainActivity() { + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + } + + public int get1(boolean condition) { + return IntermediateClass.get1(condition); + } + + public int get2(boolean condition) { + return IntermediateClass.get2(condition); + } + public int get3(boolean condition) { + return MiniIntermediateClass.get3(condition); + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java new file mode 100644 index 0000000..5957662 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/MiniIntermediateClass.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.multidexlegacyandexception; + +public class MiniIntermediateClass { + + public static int get3(boolean condition) { + ClassInSecondaryDex thrower = new ClassInSecondaryDex(condition); + try { + thrower.canThrow2(); + thrower.canThrow1(); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (ExceptionInSecondaryDex e) { + return 11; + } catch (SuperExceptionInSecondaryDex|SuperExceptionInMainDex e) { + return 23; + } + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java new file mode 100644 index 0000000..c94b30a --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInMainDex.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class SuperExceptionInMainDex extends Exception { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java new file mode 100644 index 0000000..6366fae --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/SuperExceptionInSecondaryDex.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.multidexlegacyandexception; + +public class SuperExceptionInSecondaryDex extends Exception { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java new file mode 100644 index 0000000..5e931bc --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/Test.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.multidexlegacyandexception; + +import android.test.ActivityInstrumentationTestCase2; + +/** + * Run the tests with: <code>adb shell am instrument -w + com.android.multidexlegacyandexception/android.test.InstrumentationTestRunner +</code> + */ +public class Test extends ActivityInstrumentationTestCase2<MainActivity> { + public Test() { + super(MainActivity.class); + } + + public void testExceptionInMainDex() { + assertEquals(10, TestApplication.get(true)); + } + + public void testExceptionInSecondaryDex() { + assertEquals(10, getActivity().get1(true)); + assertEquals(11, getActivity().get2(true)); + } + + public void testExceptionInIntermediate() { + assertEquals(11, IntermediateClass.get3(true)); + assertEquals(11, MiniIntermediateClass.get3(true)); + assertEquals(11, IntermediateClass.get4(true)); + assertEquals(1, IntermediateClass.get5(null)); + assertEquals(10, IntermediateClass.get5(new ExceptionInMainDex())); + assertEquals(11, IntermediateClass.get5(new ExceptionInSecondaryDex())); + assertEquals(12, IntermediateClass.get5(new ExceptionInMainDex2())); + assertEquals(13, IntermediateClass.get5(new ExceptionInSecondaryDex2())); + assertEquals(14, IntermediateClass.get5(new OutOfMemoryError())); + assertEquals(17, IntermediateClass.get5(new CaughtOnlyException())); + assertEquals(39, IntermediateClass.get5(new ExceptionInSecondaryDexWithSuperInMain())); + assertEquals(23, IntermediateClass.get5(new SuperExceptionInSecondaryDex())); + assertEquals(23, IntermediateClass.get5(new SuperExceptionInMainDex())); + assertEquals(23, IntermediateClass.get5(new CaughtOnlyByIntermediateException())); + assertEquals(37, IntermediateClass.get5(new ArrayIndexOutOfBoundsException())); + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java new file mode 100644 index 0000000..dece9a4 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/TestApplication.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.multidexlegacyandexception; + +import android.support.multidex.MultiDexApplication; + +public class TestApplication extends MultiDexApplication { + + private static void canThrow1(boolean condition) throws ExceptionInMainDex { + if (condition) { + throw new ExceptionInMainDex(); + } + } + + + public static int get(boolean condition) { + try { + canThrow1(condition); + return 1; + } catch (ExceptionInMainDex e) { + return 10; + } catch (OutOfMemoryError e) { + return 12; + } catch (CaughtOnlyException e) { + return 17; + } catch (SuperExceptionInMainDex e) { + return 27; + } + } + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java index 20fe465..7b83999 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/src/com/android/framework/multidexlegacytestservices/AbstractService.java @@ -31,7 +31,7 @@ import java.io.RandomAccessFile; * Empty service for testing legacy multidex. Access more than 64k methods but some are required at * init, some only at verification and others during execution. */ -public abstract class AbstractService extends Service { +public abstract class AbstractService extends Service implements Runnable { private final String TAG = "MultidexLegacyTestService" + getId(); private int instanceFieldNotInited; @@ -47,6 +47,12 @@ public abstract class AbstractService extends Service { @Override public void onCreate() { Log.i(TAG, "onCreate"); + new Thread(this).start(); + + } + + @Override + public void run() { Context applicationContext = getApplicationContext(); File resultFile = new File(applicationContext.getFilesDir(), getId()); try { @@ -84,7 +90,6 @@ public abstract class AbstractService extends Service { } catch (IOException e) { e.printStackTrace(); } - } @Override diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java index 0f343b1..ca68e93 100644 --- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java +++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java @@ -25,8 +25,9 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; -import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController; +import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem; +import com.android.internal.inputmethod.InputMethodUtils; import java.util.ArrayList; import java.util.Arrays; @@ -39,6 +40,7 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe private static final boolean DUMMY_FORCE_DEFAULT = false; private static final int DUMMY_IS_DEFAULT_RES_ID = 0; private static final String SYSTEM_LOCALE = "en_US"; + private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; private static InputMethodSubtype createDummySubtype(final String locale) { final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder(); @@ -64,142 +66,233 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe si.exported = true; si.nonLocalizedLabel = imeLabel; ri.serviceInfo = si; - final List<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); - for (String subtypeLocale : subtypeLocales) { - subtypes.add(createDummySubtype(subtypeLocale)); + List<InputMethodSubtype> subtypes = null; + if (subtypeLocales != null) { + subtypes = new ArrayList<InputMethodSubtype>(); + for (String subtypeLocale : subtypeLocales) { + subtypes.add(createDummySubtype(subtypeLocale)); + } } final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME, DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID, DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod); - for (int i = 0; i < subtypes.size(); ++i) { - final String subtypeLocale = subtypeLocales.get(i); - items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale, - SYSTEM_LOCALE)); + if (subtypes == null) { + items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi, + NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE)); + } else { + for (int i = 0; i < subtypes.size(); ++i) { + final String subtypeLocale = subtypeLocales.get(i); + items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale, + SYSTEM_LOCALE)); + } } } - private static List<ImeSubtypeListItem> createTestData() { + private static List<ImeSubtypeListItem> createEnabledImeSubtypes() { final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>(); - addDummyImeSubtypeListItems(items, "switchAwareLatinIme", "switchAwareLatinIme", - Arrays.asList("en_US", "es_US", "fr"), + addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"), true /* supportsSwitchingToNextInputMethod*/); - addDummyImeSubtypeListItems(items, "nonSwitchAwareLatinIme", "nonSwitchAwareLatinIme", + addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme", Arrays.asList("en_UK", "hi"), false /* supportsSwitchingToNextInputMethod*/); - addDummyImeSubtypeListItems(items, "switchAwareJapaneseIme", "switchAwareJapaneseIme", - Arrays.asList("ja_JP"), + addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null, + false /* supportsSwitchingToNextInputMethod*/); + addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"), true /* supportsSwitchingToNextInputMethod*/); - addDummyImeSubtypeListItems(items, "nonSwitchAwareJapaneseIme", "nonSwitchAwareJapaneseIme", - Arrays.asList("ja_JP"), + addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme", + Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/); + return items; + } + + private static List<ImeSubtypeListItem> createDisabledImeSubtypes() { + final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>(); + addDummyImeSubtypeListItems(items, + "UnknownIme", "UnknownIme", + Arrays.asList("en_US", "hi"), + true /* supportsSwitchingToNextInputMethod*/); + addDummyImeSubtypeListItems(items, + "UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme", + Arrays.asList("en_US"), + false /* supportsSwitchingToNextInputMethod*/); + addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme", + "UnknownSubtypeUnawareIme", null, false /* supportsSwitchingToNextInputMethod*/); return items; } + private void assertNextInputMethod(final ControllerImpl controller, + final boolean onlyCurrentIme, + final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) { + InputMethodSubtype subtype = null; + if (currentItem.mSubtypeName != null) { + subtype = createDummySubtype(currentItem.mSubtypeName.toString()); + } + final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme, + currentItem.mImi, subtype); + assertEquals(nextItem, nextIme); + } + + private void assertRotationOrder(final ControllerImpl controller, + final boolean onlyCurrentIme, + final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) { + final int N = expectedRotationOrderOfImeSubtypeList.length; + for (int i = 0; i < N; i++) { + final int currentIndex = i; + final int nextIndex = (currentIndex + 1) % N; + final ImeSubtypeListItem currentItem = + expectedRotationOrderOfImeSubtypeList[currentIndex]; + final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex]; + assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem); + } + } + + private void onUserAction(final ControllerImpl controller, + final ImeSubtypeListItem subtypeListItem) { + InputMethodSubtype subtype = null; + if (subtypeListItem.mSubtypeName != null) { + subtype = createDummySubtype(subtypeListItem.mSubtypeName.toString()); + } + controller.onUserActionLocked(subtypeListItem.mImi, subtype); + } + @SmallTest - public void testGetNextInputMethodImplWithNotOnlyCurrentIme() throws Exception { - final List<ImeSubtypeListItem> imList = createTestData(); - - final boolean ONLY_CURRENT_IME = false; - ImeSubtypeListItem currentIme; - ImeSubtypeListItem nextIme; - - // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US" - currentIme = imList.get(0); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(1), nextIme); - // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr" - currentIme = imList.get(1); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(2), nextIme); - // "switchAwareLatinIme/fr" -> "switchAwareJapaneseIme/ja_JP" - currentIme = imList.get(2); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(5), nextIme); - // "switchAwareJapaneseIme/ja_JP" -> "switchAwareLatinIme/en_US" - currentIme = imList.get(5); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(0), nextIme); - - // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi" - currentIme = imList.get(3); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(4), nextIme); - // "nonSwitchAwareLatinIme/hi" -> "nonSwitchAwareJapaneseIme/ja_JP" - currentIme = imList.get(4); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(6), nextIme); - // "nonSwitchAwareJapaneseIme/ja_JP" -> "nonSwitchAwareLatinIme/en_UK" - currentIme = imList.get(6); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(3), nextIme); + public void testControllerImpl() throws Exception { + final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes(); + final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0); + final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1); + final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2); + final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3); + + final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes(); + final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0); + final ImeSubtypeListItem latinIme_fr = enabledItems.get(1); + final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2); + final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3); + final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4); + final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5); + final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6); + + final ControllerImpl controller = ControllerImpl.createFrom( + null /* currentInstance */, enabledItems); + + // switching-aware loop + assertRotationOrder(controller, false /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr, japaneseIme_ja_JP); + + // switching-unaware loop + assertRotationOrder(controller, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + + // test onlyCurrentIme == true + assertRotationOrder(controller, true /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr); + assertRotationOrder(controller, true /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + subtypeUnawareIme, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + japaneseIme_ja_JP, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + switchUnawareJapaneseIme_ja_JP, null); + + // Make sure that disabled IMEs are not accepted. + assertNextInputMethod(controller, false /* onlyCurrentIme */, + disabledIme_en_US, null); + assertNextInputMethod(controller, false /* onlyCurrentIme */, + disabledIme_hi, null); + assertNextInputMethod(controller, false /* onlyCurrentIme */, + disabledSwitchingUnawareIme, null); + assertNextInputMethod(controller, false /* onlyCurrentIme */, + disabledSubtypeUnawareIme, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + disabledIme_en_US, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + disabledIme_hi, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + disabledSwitchingUnawareIme, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + disabledSubtypeUnawareIme, null); } @SmallTest - public void testGetNextInputMethodImplWithOnlyCurrentIme() throws Exception { - final List<ImeSubtypeListItem> imList = createTestData(); - - final boolean ONLY_CURRENT_IME = true; - ImeSubtypeListItem currentIme; - ImeSubtypeListItem nextIme; - - // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US" - currentIme = imList.get(0); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(1), nextIme); - // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr" - currentIme = imList.get(1); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(2), nextIme); - // "switchAwareLatinIme/fr" -> "switchAwareLatinIme/en_US" - currentIme = imList.get(2); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(0), nextIme); - - // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi" - currentIme = imList.get(3); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(4), nextIme); - // "nonSwitchAwareLatinIme/hi" -> "switchAwareLatinIme/en_UK" - currentIme = imList.get(4); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertEquals(imList.get(3), nextIme); - - // "switchAwareJapaneseIme/ja_JP" -> null - currentIme = imList.get(5); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertNull(nextIme); - - // "nonSwitchAwareJapaneseIme/ja_JP" -> null - currentIme = imList.get(6); - nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodLockedImpl( - imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype( - currentIme.mSubtypeName.toString())); - assertNull(nextIme); + public void testControllerImplWithUserAction() throws Exception { + final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes(); + final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0); + final ImeSubtypeListItem latinIme_fr = enabledItems.get(1); + final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2); + final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3); + final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4); + final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5); + final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6); + + final ControllerImpl controller = ControllerImpl.createFrom( + null /* currentInstance */, enabledItems); + + // === switching-aware loop === + assertRotationOrder(controller, false /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr, japaneseIme_ja_JP); + // Then notify that a user did something for latinIme_fr. + onUserAction(controller, latinIme_fr); + assertRotationOrder(controller, false /* onlyCurrentIme */, + latinIme_fr, latinIme_en_US, japaneseIme_ja_JP); + // Then notify that a user did something for latinIme_fr again. + onUserAction(controller, latinIme_fr); + assertRotationOrder(controller, false /* onlyCurrentIme */, + latinIme_fr, latinIme_en_US, japaneseIme_ja_JP); + // Then notify that a user did something for japaneseIme_ja_JP. + onUserAction(controller, latinIme_fr); + assertRotationOrder(controller, false /* onlyCurrentIme */, + japaneseIme_ja_JP, latinIme_fr, latinIme_en_US); + // Check onlyCurrentIme == true. + assertNextInputMethod(controller, true /* onlyCurrentIme */, + japaneseIme_ja_JP, null); + assertRotationOrder(controller, true /* onlyCurrentIme */, + latinIme_fr, latinIme_en_US); + assertRotationOrder(controller, true /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr); + + // === switching-unaware loop === + assertRotationOrder(controller, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + // User action should be ignored for switching unaware IMEs. + onUserAction(controller, switchingUnawarelatinIme_hi); + assertRotationOrder(controller, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + // User action should be ignored for switching unaware IMEs. + onUserAction(controller, switchUnawareJapaneseIme_ja_JP); + assertRotationOrder(controller, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + // Check onlyCurrentIme == true. + assertRotationOrder(controller, true /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + subtypeUnawareIme, null); + assertNextInputMethod(controller, true /* onlyCurrentIme */, + switchUnawareJapaneseIme_ja_JP, null); + + // Rotation order should be preserved when created with the same subtype list. + final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes(); + final ControllerImpl newController = ControllerImpl.createFrom(controller, + sameEnabledItems); + assertRotationOrder(newController, false /* onlyCurrentIme */, + japaneseIme_ja_JP, latinIme_fr, latinIme_en_US); + assertRotationOrder(newController, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + + // Rotation order should be initialized when created with a different subtype list. + final List<ImeSubtypeListItem> differentEnabledItems = Arrays.asList( + latinIme_en_US, latinIme_fr, switchingUnawarelatinIme_en_UK, + switchUnawareJapaneseIme_ja_JP); + final ControllerImpl anotherController = ControllerImpl.createFrom(controller, + differentEnabledItems); + assertRotationOrder(anotherController, false /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr); + assertRotationOrder(anotherController, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP); } - } +} diff --git a/data/keyboards/Vendor_2378_Product_1008.kl b/data/keyboards/Vendor_2378_Product_1008.kl new file mode 100644 index 0000000..478da03 --- /dev/null +++ b/data/keyboards/Vendor_2378_Product_1008.kl @@ -0,0 +1,35 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# OnLive, Inc. OnLive Wireless Controller, USB adapter + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_SELECT +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/docs/html/community/index.html b/docs/html/community/index.html index eeb1c51..e3834ba 100644 --- a/docs/html/community/index.html +++ b/docs/html/community/index.html @@ -34,6 +34,9 @@ href="//fonts.googleapis.com/css?family=Roboto:regular,medium,thin,italic,medium </script> <style> +#header { + padding: 2.2em 0 0.2em 0; +} #header-wrap h1 { margin:0; padding:0; diff --git a/docs/html/distribute/engage/app-updates.jd b/docs/html/distribute/engage/app-updates.jd index 6b751b9..2b7cd2c 100644 --- a/docs/html/distribute/engage/app-updates.jd +++ b/docs/html/distribute/engage/app-updates.jd @@ -36,12 +36,12 @@ page.image=/images/gp-your-user-0.jpg "18x6," data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" data-query= diff --git a/docs/html/distribute/engage/community.jd b/docs/html/distribute/engage/community.jd index 035058a..e202d54 100644 --- a/docs/html/distribute/engage/community.jd +++ b/docs/html/distribute/engage/community.jd @@ -29,12 +29,14 @@ page.image=/images/gp-engage-9.jpg Learn more about how to <a href="{@docRoot}distribute/users/build-community.html">build and manage a community</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<p style="clear:both"> +</p> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" data-query= diff --git a/docs/html/distribute/engage/deep-linking.jd b/docs/html/distribute/engage/deep-linking.jd index cd62f9d..50728c5 100644 --- a/docs/html/distribute/engage/deep-linking.jd +++ b/docs/html/distribute/engage/deep-linking.jd @@ -11,7 +11,7 @@ page.image=/images/gp-listing-4.jpg </p> <div class="headerLine"> -<h1>Deep Linking from Google+ Posts</h1><hr> +<h2>Deep Linking from Google+ Posts</h2> </div> <p> @@ -43,8 +43,8 @@ page.image=/images/gp-listing-4.jpg </div> -<div class="headerLine clearfloat"> -<h1>Deep Linking from Google Search — App Indexing</h1><hr> +<div class="headerLine"> +<h2>Deep Linking from Google Search — App Indexing</h2> </div> <p> @@ -64,12 +64,12 @@ page.image=/images/gp-listing-4.jpg <img src="{@docRoot}images/gp-listing-4.jpg" style="padding-top:1em;"> </div> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" data-query= diff --git a/docs/html/distribute/engage/easy-signin.jd b/docs/html/distribute/engage/easy-signin.jd index 92c3ffc..d066181 100644 --- a/docs/html/distribute/engage/easy-signin.jd +++ b/docs/html/distribute/engage/easy-signin.jd @@ -37,11 +37,11 @@ page.image=/images/google/gps-googleplus.png </p> <div class="headerLine"> - <h1> + <h2> And Spreading the Word a Snap - </h1> + </h2> + - <hr> </div> @@ -85,12 +85,14 @@ page.image=/images/google/gps-googleplus.png </li> </ul> - <div class="headerLine clearfloat"> - <h1 id="related-resources"> +<p style="clear:both"> +</p> + <div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/engage/game-services.jd b/docs/html/distribute/engage/game-services.jd index 5153435..1c77d2d 100644 --- a/docs/html/distribute/engage/game-services.jd +++ b/docs/html/distribute/engage/game-services.jd @@ -75,12 +75,12 @@ page.image=/images/google/gps-play_games_logo.png Game Developer Best Practices</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" data-query= diff --git a/docs/html/distribute/engage/gcm.jd b/docs/html/distribute/engage/gcm.jd index d793124e..7d9b6bb 100644 --- a/docs/html/distribute/engage/gcm.jd +++ b/docs/html/distribute/engage/gcm.jd @@ -35,12 +35,12 @@ page.image=/images/gcm/gcm-logo.png free and there are no quotas. </p> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/engage/notifications.jd b/docs/html/distribute/engage/notifications.jd index fecfb45..1aa0637 100644 --- a/docs/html/distribute/engage/notifications.jd +++ b/docs/html/distribute/engage/notifications.jd @@ -40,17 +40,15 @@ page.image=/design/media/notifications_pattern_anatomy.png </p> - <div class="sidebox" style="width:326px;float:left;margin-left:0"> <p><strong>Tip:</strong> Use notifications sparingly — be sure any information presented is useful. Give users the option to turn notifications off. </p> - </div> - <div class="headerLine clearfloat"> - <h1 id="related-resources"> + <div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1><hr> + </h2> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/engage/video.jd b/docs/html/distribute/engage/video.jd index 1a30f3a..c5a4997 100644 --- a/docs/html/distribute/engage/video.jd +++ b/docs/html/distribute/engage/video.jd @@ -23,12 +23,12 @@ page.image=/images/gp-engage-smule.jpg <img src="{@docRoot}images/gp-engage-smule.jpg"> </div> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" data-query= diff --git a/docs/html/distribute/engage/widgets.jd b/docs/html/distribute/engage/widgets.jd index b17af08..286adea 100644 --- a/docs/html/distribute/engage/widgets.jd +++ b/docs/html/distribute/engage/widgets.jd @@ -28,10 +28,13 @@ page.image=/images/gp-engage-0.jpg or upcoming deadlines. Widgets should serve as more than a launcher icon.</p> </div> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<p style="clear:both"> +</p> + +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1><hr> + </h2> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/essentials/best-practices/apps.jd b/docs/html/distribute/essentials/best-practices/apps.jd index 055a349..bbac727 100644 --- a/docs/html/distribute/essentials/best-practices/apps.jd +++ b/docs/html/distribute/essentials/best-practices/apps.jd @@ -17,7 +17,7 @@ page.metaDescription=Essential tips for launching successful apps in Google Play <p>The following best practices have enabled developers worldwide to build great, successful apps for Google Play.</p> <div class="headerLine"> -<h1 id="essentials">Get the Essentials Right</h1><hr> +<h2 id="essentials">Get the Essentials Right</h2> </div> <h3>1. Make it Android</h3> @@ -82,11 +82,11 @@ page.metaDescription=Essential tips for launching successful apps in Google Play </ul> <div class="headerLine"> - <h1 id="users"> + <h2 id="users"> Get Users - </h1> + </h2> + - <hr> </div> <h3> @@ -150,11 +150,11 @@ page.metaDescription=Essential tips for launching successful apps in Google Play </ul> <div class="headerLine"> - <h1 id="engage"> + <h2 id="engage"> Engage and Retain - </h1> + </h2> + - <hr> </div> <h3> @@ -211,11 +211,11 @@ page.metaDescription=Essential tips for launching successful apps in Google Play </ul> <div class="headerLine"> - <h1 id="beyond"> + <h2 id="beyond"> Beyond the Basics - </h1> + </h2> + - <hr> </div> <ul> @@ -249,7 +249,7 @@ page.metaDescription=Essential tips for launching successful apps in Google Play </ul> <div class="headerLine"> -<h1 id="related-resources">Related Resources</h1><hr> +<h2 id="related-resources">Related Resources</h2> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/essentials/best-practices/games.jd b/docs/html/distribute/essentials/best-practices/games.jd index ac1df44..c4ce66e 100644 --- a/docs/html/distribute/essentials/best-practices/games.jd +++ b/docs/html/distribute/essentials/best-practices/games.jd @@ -20,11 +20,11 @@ page.metaDescription=Essential tips for launching successful games in Google Pla </p> <div class="headerLine"> - <h1 id="users"> + <h2 id="users"> Get Users - </h1> + </h2> + - <hr> </div> <h3> @@ -111,11 +111,11 @@ page.metaDescription=Essential tips for launching successful games in Google Pla </ul> <div class="headerLine"> - <h1 id="engage"> + <h2 id="engage"> Engage and Retain - </h1> + </h2> + - <hr> </div> <h3> @@ -213,11 +213,11 @@ page.metaDescription=Essential tips for launching successful games in Google Pla </ul> <div class="headerLine"> - <h1 id="beyond"> + <h2 id="beyond"> Beyond the Basics - </h1> + </h2> + - <hr> </div> <ul> @@ -249,7 +249,7 @@ page.metaDescription=Essential tips for launching successful games in Google Pla </ul> <div class="headerLine"> -<h1 id="related-resources">Related Resources</h1><hr> +<h2 id="related-resources">Related Resources</h2> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/essentials/gpfe-guidelines.jd b/docs/html/distribute/essentials/gpfe-guidelines.jd index 799009f..734bddc 100644 --- a/docs/html/distribute/essentials/gpfe-guidelines.jd +++ b/docs/html/distribute/essentials/gpfe-guidelines.jd @@ -50,12 +50,12 @@ Xnonavpage=true Distribution Agreement</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="basic-reqts"> +<div class="headerLine"> + <h2 id="basic-reqts"> Basic Requirements - </h1> + </h2> + - <hr> </div> <p> @@ -108,11 +108,11 @@ Xnonavpage=true </ul> <div class="headerLine"> - <h1 id="monetizing-ads"> + <h2 id="monetizing-ads"> Monetizing and Ads - </h1> + </h2> + - <hr> </div> <p> @@ -195,11 +195,11 @@ Xnonavpage=true </ul> <div class="headerLine"> - <h1 id="e-value"> + <h2 id="e-value"> Educational Value - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -299,11 +299,11 @@ Xnonavpage=true </p> <div class="headerLine"> - <h1 id="quality"> + <h2 id="quality"> App Quality - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -410,11 +410,11 @@ Xnonavpage=true </ul> <div class="headerLine"> - <h1 id="test-environment"> + <h2 id="test-environment"> Test Environment - </h1> + </h2> + - <hr> </div> <p> @@ -487,7 +487,7 @@ Xnonavpage=true </ul> <div class="headerLine"> -<h1>Related Resources</h1><hr> +<h2>Related Resources</h2> </div> <div class="dynamic-grid"> diff --git a/docs/html/distribute/essentials/optimizing-your-app.jd b/docs/html/distribute/essentials/optimizing-your-app.jd index 3fe91b28..696ef53 100644 --- a/docs/html/distribute/essentials/optimizing-your-app.jd +++ b/docs/html/distribute/essentials/optimizing-your-app.jd @@ -53,11 +53,11 @@ page.image=/distribute/images/gp-optimize-card.jpg </p> <div class="headerLine"> - <h1 id="listen-to-your-users"> + <h2 id="listen-to-your-users"> Listen to Your Users - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -147,11 +147,11 @@ page.image=/distribute/images/gp-optimize-card.jpg </p> <div class="headerLine" id="measuring-analyzing-responding"> - <h1> + <h2> Measuring, Analyzing, and Responding to User Behavior - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -260,11 +260,11 @@ who can help automate, measure, and optimize your mobile marketing. </p> <div class="headerLine"> - <h1 id="improve-stability"> + <h2 id="improve-stability"> Improve Stability and Eliminate Bugs - </h1> + </h2> + - <hr> </div> <p> @@ -298,11 +298,11 @@ who can help automate, measure, and optimize your mobile marketing. </p> <div class="headerLine"> - <h1 id="improve-ui"> + <h2 id="improve-ui"> Improve UI Responsiveness - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -352,11 +352,11 @@ who can help automate, measure, and optimize your mobile marketing. </p> <div class="headerLine"> - <h1 id="improve-usability"> + <h2 id="improve-usability"> Improve Usability - </h1> + </h2> + - <hr> </div> <div class="sidebox-wrapper" style="float:right;"> @@ -403,11 +403,11 @@ who can help automate, measure, and optimize your mobile marketing. </p> <div class="headerLine"> - <h1 id="professional-appearance"> + <h2 id="professional-appearance"> Professional Appearance and Aesthetics - </h1> + </h2> + - <hr> </div> <p> @@ -439,11 +439,11 @@ who can help automate, measure, and optimize your mobile marketing. </p> <div class="headerLine"> - <h1 id="deliver-features"> + <h2 id="deliver-features"> Deliver the Right Set of Features - </h1> + </h2> + - <hr> </div> <p> @@ -464,11 +464,11 @@ who can help automate, measure, and optimize your mobile marketing. </p> <div class="headerLine"> - <h1 id="integrate"> + <h2 id="integrate"> Integrate with the System and Third-Party apps - </h1> + </h2> + - <hr> </div> <p> @@ -502,7 +502,7 @@ who can help automate, measure, and optimize your mobile marketing. </p> <div class="headerLine"> -<h1 id="related-resources">Related Resources</h1><hr> +<h2 id="related-resources">Related Resources</h2> </div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/optimizing, tag:addia" diff --git a/docs/html/distribute/essentials/quality/core.jd b/docs/html/distribute/essentials/quality/core.jd index 558b030..c301f8c 100644 --- a/docs/html/distribute/essentials/quality/core.jd +++ b/docs/html/distribute/essentials/quality/core.jd @@ -63,12 +63,12 @@ page.image=/distribute/images/core-quality-guidelines.jpg Guidelines</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="ux"> +<div class="headerLine"> + <h2 id="ux"> Visual Design and User Interaction - </h1> + </h2> + - <hr> </div> <p> @@ -244,12 +244,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,6x3,6x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="fn"> +<div class="headerLine"> + <h2 id="fn"> Functionality - </h1> + </h2> + - <hr> </div> <p> @@ -518,12 +518,12 @@ data-maxresults="6"> data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="ps"> +<div class="headerLine"> + <h2 id="ps"> Performance and Stability - </h1> + </h2> + - <hr> </div> <p> @@ -679,12 +679,12 @@ data-sortorder="-timestamp" data-cardsizes="6x3" data-maxresults="6"> data-cardsizes="6x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="listing"> +<div class="headerLine"> + <h2 id="listing"> Google Play - </h1> + </h2> + - <hr> </div> <p> @@ -828,12 +828,12 @@ data-cardsizes="6x3" data-maxresults="6"> data-cardsizes="6x3,6x3,6x3,6x3,6x3,6x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="test-environment"> +<div class="headerLine"> + <h2 id="test-environment"> Setting Up a Test Environment - </h1> + </h2> + - <hr> </div> <p> @@ -863,12 +863,12 @@ data-cardsizes="6x3,6x3,6x3,6x3,6x3,6x3" data-maxresults="6"> increase the number or complexity of tests and quality criteria. </p> -<div class="headerLine clearfloat"> - <h1 id="tests"> +<div class="headerLine"> + <h2 id="tests"> Test Procedures - </h1> + </h2> + - <hr> </div> <p> diff --git a/docs/html/distribute/essentials/quality/tablets.jd b/docs/html/distribute/essentials/quality/tablets.jd index 7dfab48..966c462 100644 --- a/docs/html/distribute/essentials/quality/tablets.jd +++ b/docs/html/distribute/essentials/quality/tablets.jd @@ -52,7 +52,7 @@ Xnonavpage=true help you address each recommendation included. </p> -<div class="headerLine"><h1 id="core-app-quality">1. Test for Basic Tablet App Quality</h1><hr></div> +<div class="headerLine"><h2 id="core-app-quality">1. Test for Basic Tablet App Quality</h2></div> <p>The first step in delivering a great tablet app experience is making sure that it meets the <em>core app quality criteria</em> for all of the devices @@ -78,8 +78,8 @@ Before publishing, also ensure that your app passes the basic technical checks a Tips page</a>.</p> -<div class="headerLine clearfloat"> -<h1 id="optimize-layouts">2. Optimize Layouts for Larger Screens</h1><hr></div> +<div class="headerLine"> +<h2 id="optimize-layouts">2. Optimize Layouts for Larger Screens</h2></div> <p> Android makes it easy to develop an app that runs well on a wide range of @@ -167,7 +167,7 @@ multi-pane UI for tablets (see next section).</li> data-maxResults="6"></div> -<div class="headerLine clearfloat"><h1 id="use-extra-space">3. Take Advantage of Extra Screen Area</h1><hr></div> +<div class="headerLine"><h2 id="use-extra-space">3. Take Advantage of Extra Screen Area</h2></div> <div style="width:340px;float:right;margin:1.5em;margin-bottom:0;margin-top:0;"> <img src="{@docRoot}images/training/app-navigation-multiple-sizes-multipane-good.png" @@ -227,7 +227,7 @@ different layouts in the appropriate screen size buckets (such as data-cardSizes="6x3,6x3,6x3" data-maxResults="6"></div> -<div class="headerLine clearfloat"><h1 id="use-tablet-icons">4. Use Assets Designed for Tablet Screens</h1><hr></div> +<div class="headerLine"><h2 id="use-tablet-icons">4. Use Assets Designed for Tablet Screens</h2></div> <div><img src="{@docRoot}design/media/devices_displays_density@2x.png"></div> @@ -316,8 +316,8 @@ it will request the {@code xxhdpi} version of the launcher icon.</li> data-cardSizes="9x3" data-maxResults="6"></div> -<div class="headerLine clearfloat"><h1 id="adjust-font-sizes">5. -Adjust Font Sizes and Touch Targets</h1><hr></div> +<div class="headerLine"><h2 id="adjust-font-sizes">5. +Adjust Font Sizes and Touch Targets</h2></div> <p>To make sure your app is easy to use on tablets, take some time to adjust the font sizes and touch targets in your tablet UI, for all of the screen @@ -353,7 +353,7 @@ or just centering the icon within the transparent button.</li> data-cardSizes="9x3,9x3,6x3,6x3,6x3" data-maxResults="6"></div> -<div class="headerLine clearfloat"><h1 id="adjust-widgets">6. Adjust Sizes of Home Screen Widgets</h1><hr></div> +<div class="headerLine"><h2 id="adjust-widgets">6. Adjust Sizes of Home Screen Widgets</h2></div> <p>If your app includes a home screen widget, here are a few points to consider to ensure a great user experience on tablet screens: </p> @@ -380,7 +380,7 @@ possible.</li> data-maxResults="6"></div> -<div class="headerLine clearfloat"><h1 id="offer-full-feature-set">7. Full Feature Set for Tablet Users</h1><hr></div> +<div class="headerLine"><h2 id="offer-full-feature-set">7. Full Feature Set for Tablet Users</h2></div> <div class="centered-full-image" style="width:600px;margin:1.5em"><img src="{@docRoot}images/gp-tablets-full-feature-set.png" alt="Tablet feature sets"></div> @@ -415,7 +415,7 @@ some recommendations:</p> </li> </ul> -<div class="headerLine clearfloat"><h1 id="android-versions">8. Target Android Versions Properly</h1><hr></div> +<div class="headerLine"><h2 id="android-versions">8. Target Android Versions Properly</h2></div> <p> To ensure the broadest possible distribution to tablets, make sure that your @@ -466,7 +466,7 @@ some recommendations:</p> data-cardSizes="6x3" data-maxResults="6"></div> -<div class="headerLine clearfloat"><h1 id="hardware-requirements">9. Declare Hardware Feature Dependencies Properly</h1><hr></div> +<div class="headerLine"><h2 id="hardware-requirements">9. Declare Hardware Feature Dependencies Properly</h2></div> <p> Handsets and tablets typically offer slightly different hardware support for @@ -536,7 +536,7 @@ permissions, make sure to explicitly declare a corresponding data-cardSizes="9x3" data-maxResults="6"></div> -<div class="headerLine clearfloat"><h1 id="support-screens">10. Declare Support for Tablet Screens</h1><hr></div> +<div class="headerLine"><h2 id="support-screens">10. Declare Support for Tablet Screens</h2></div> <p>To ensure that you can distribute your app to a broad range of tablets, your app should declare support for tablet screen sizes in its manifest file, as follows:</p> @@ -569,7 +569,7 @@ element in your app.</p> data-maxResults="6"></div> -<div class="headerLine clearfloat"><h1 id="google-play">11. Showcase Your Tablet UI in Google Play</h1><hr></div> +<div class="headerLine"><h2 id="google-play">11. Showcase Your Tablet UI in Google Play</h2></div> <p> After you've done the work to create an rich, optimized UI for your tablet @@ -697,12 +697,12 @@ element in your app.</p> data-cardSizes="9x3,9x3,9x3,9x3" data-maxResults="6"></div> -<div class="headerLine clearfloat"> - <h1 id="google-play-best-practices"> +<div class="headerLine"> + <h2 id="google-play-best-practices"> 12. Follow Best Practices for Publishing in Google Play - </h1> + </h2> + - <hr> </div> <p> @@ -807,12 +807,12 @@ appropriate.</p> data-maxResults="6"></div> -<div class="headerLine clearfloat"> - <h1 id="test-environment"> +<div class="headerLine"> + <h2 id="test-environment"> Setting Up a Test Environment for Tablets - </h1> + </h2> + - <hr> </div> <p> @@ -858,7 +858,7 @@ listed platform versions, screen configurations, and hardware feature configurat </tr> </table> -<div class="headerLine clearfloat"><h1 id="related-resources">Related Resources</h1><hr></div> +<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/essentials/tabletguidelines" diff --git a/docs/html/distribute/googleplay/about.jd b/docs/html/distribute/googleplay/about.jd index cf0c6d2..c7c91ac 100644 --- a/docs/html/distribute/googleplay/about.jd +++ b/docs/html/distribute/googleplay/about.jd @@ -58,11 +58,11 @@ page.image=/distribute/images/about-play.jpg </p> <div class="headerLine"> - <h1 id="ratings-reviews"> + <h2 id="ratings-reviews"> User Ratings and Reviews - </h1> + </h2> + - <hr> </div> <p> @@ -83,11 +83,11 @@ page.image=/distribute/images/about-play.jpg </div> <div class="headerLine"> - <h1 id="category-browsing"> + <h2 id="category-browsing"> Category Browsing - </h1> + </h2> + - <hr> </div> <p> @@ -98,11 +98,11 @@ page.image=/distribute/images/about-play.jpg </p> <div class="headerLine"> - <h1 id="search"> + <h2 id="search"> Search - </h1> + </h2> + - <hr> </div> <p> @@ -113,11 +113,11 @@ page.image=/distribute/images/about-play.jpg </p> <div class="headerLine"> - <h1 id="top-charts-and-lists"> + <h2 id="top-charts-and-lists"> Top Charts and Lists - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -195,11 +195,11 @@ page.image=/distribute/images/about-play.jpg </table> <div class="headerLine"> - <h1 id="featured-staff-picks"> + <h2 id="featured-staff-picks"> Featured, Staff Picks, Collections, and Badges - </h1> + </h2> + - <hr> </div> <p> @@ -313,11 +313,11 @@ page.image=/distribute/images/about-play.jpg </p> <div class="headerLine"> - <h1 id="product-detail-pages"> + <h2 id="product-detail-pages"> Store Listing Pages - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -357,8 +357,11 @@ page.image=/distribute/images/about-play.jpg Products</a> to find out how. </p> -<div class="headerLine clearfloat"> -<h1>Related Resources</h1><hr> +<p style="clear:both"> +</p> + +<div class="headerLine"> +<h2>Related Resources</h2> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/googleplay/developer-console.jd b/docs/html/distribute/googleplay/developer-console.jd index 6263431..f5b3ac6 100644 --- a/docs/html/distribute/googleplay/developer-console.jd +++ b/docs/html/distribute/googleplay/developer-console.jd @@ -44,12 +44,12 @@ Xnonavpage=true verification by email, you can sign in to your Google Play Developer Console. </p> -<div class="headerLine clearfloat"> - <h1 id="allapps"> +<div class="headerLine"> + <h2 id="allapps"> All Applications - </h1> + </h2> + - <hr> </div> <p> @@ -61,12 +61,12 @@ Xnonavpage=true <img src="{@docRoot}images/gp-dc-home.png" class="border-img"> </div> -<div class="headerLine clearfloat" style="margin-top:-6px"> - <h1 id="account-details"> +<div class="headerLine" style="margin-top:-6px"> + <h2 id="account-details"> Your Account Details - </h1> + </h2> + - <hr> </div> <p> @@ -112,12 +112,12 @@ Xnonavpage=true Google Play licensing. </p> -<div class="headerLine clearfloat"> - <h1 id="merchant-account"> +<div class="headerLine"> + <h2 id="merchant-account"> Linking Your Merchant Account - </h1> + </h2> + - <hr> </div> <p> @@ -127,12 +127,12 @@ Xnonavpage=true from sales. </p> -<div class="headerLine clearfloat"> - <h1 id="multiple-user-accounts"> +<div class="headerLine"> + <h2 id="multiple-user-accounts"> Multiple User Accounts - </h1> + </h2> + - <hr> </div> <p> @@ -157,12 +157,12 @@ Xnonavpage=true up multiple accounts</a> now. </p> -<div class="headerLine clearfloat"> - <h1 id="store-listing-details"> +<div class="headerLine"> + <h2 id="store-listing-details"> Store Listing Details - </h1> + </h2> + - <hr> </div> <p> @@ -181,12 +181,12 @@ Xnonavpage=true <img src="{@docRoot}images/gp-dc-details.png" class="frame"> </div> -<div class="headerLine clearfloat"> - <h1 id="upload-instantly-publish"> +<div class="headerLine"> + <h2 id="upload-instantly-publish"> Upload and Instantly Publish - </h1> + </h2> + - <hr> </div> <p> @@ -212,12 +212,12 @@ Xnonavpage=true time. </p> -<div class="headerLine clearfloat"> - <h1 id="alpha-beta"> +<div class="headerLine"> + <h2 id="alpha-beta"> Alpha and Beta Testing - </h1> + </h2> + - <hr> </div> <p> @@ -270,12 +270,12 @@ Xnonavpage=true Checklist</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="staged-rollouts"> +<div class="headerLine"> + <h2 id="staged-rollouts"> Staged Rollouts - </h1> + </h2> + - <hr> </div> <p> @@ -303,12 +303,12 @@ Xnonavpage=true updates. </p> -<div class="headerLine clearfloat"> - <h1 id="multiple-apk"> +<div class="headerLine"> + <h2 id="multiple-apk"> Multiple APK Support - </h1> + </h2> + - <hr> </div> <p> @@ -335,12 +335,12 @@ Xnonavpage=true of the normal app installation. </p> -<div class="headerLine clearfloat"> - <h1 id="selling-pricing-your-products"> +<div class="headerLine"> + <h2 id="selling-pricing-your-products"> Selling and Pricing Your Products - </h1> + </h2> + - <hr> </div> <div class="figure-right"> @@ -407,12 +407,12 @@ Xnonavpage=true Expand into New Markets</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="in-app-products"> +<div class="headerLine"> + <h2 id="in-app-products"> In-app Products - </h1> + </h2> + - <hr> </div> <p> @@ -448,12 +448,12 @@ Xnonavpage=true monetization models </p> -<div class="headerLine clearfloat"> - <h1 id="distribution-controls"> +<div class="headerLine"> + <h2 id="distribution-controls"> Distribution Controls - </h1> + </h2> + - <hr> </div> <p> @@ -518,12 +518,12 @@ Xnonavpage=true exclude specific devices if needed. </p> -<div class="headerLine clearfloat"> - <h1 id="reviews-reports"> +<div class="headerLine"> + <h2 id="reviews-reports"> User Reviews and Crash Reports - </h1> + </h2> + - <hr> </div> <div class="figure-right" style="width:500px;"> @@ -548,12 +548,12 @@ Xnonavpage=true devices. </p> -<div class="headerLine clearfloat"> - <h1 id="app-stats"> +<div class="headerLine"> + <h2 id="app-stats"> App Statistics - </h1> + </h2> + - <hr> </div> <div class="figure" style="width:500px"> @@ -587,9 +587,12 @@ Xnonavpage=true on data inside a dimension by adding specific points to the timeline. </p> +<p style="clear:both"> +</p> + <div class="dynamic-grid"> -<div class="headerLine clearfloat"> -<h1 id="related-resources">Related Resources</h1><hr/> +<div class="headerLine"> +<h2 id="related-resources">Related Resources</h2> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/googleplay/edu/about.jd b/docs/html/distribute/googleplay/edu/about.jd index 3944909..1c003cf 100644 --- a/docs/html/distribute/googleplay/edu/about.jd +++ b/docs/html/distribute/googleplay/edu/about.jd @@ -106,8 +106,10 @@ Xnonavpage=true </div> </div> -<div class="headerLine clearfloat"> -<h1>Related Resources</h1><hr> +<p style="clear:both"> +</p> +<div class="headerLine"> +<h2>Related Resources</h2> </div> <div class="dynamic-grid"> diff --git a/docs/html/distribute/googleplay/edu/faq.jd b/docs/html/distribute/googleplay/edu/faq.jd index 0866da5..36e2064 100644 --- a/docs/html/distribute/googleplay/edu/faq.jd +++ b/docs/html/distribute/googleplay/edu/faq.jd @@ -58,11 +58,11 @@ page.image=/distribute/images/gpfe-faq.jpg </p> <div class="headerLine"> - <h1 id="business-model-and-monetization"> + <h2 id="business-model-and-monetization"> Business Model and Monetization - </h1> + </h2> + - <hr> </div> <p> @@ -143,11 +143,11 @@ page.image=/distribute/images/gpfe-faq.jpg </p> <div class="headerLine"> - <h1 id="free-trials"> + <h2 id="free-trials"> Free Trials - </h1> + </h2> + - <hr> </div> <p> @@ -172,11 +172,11 @@ page.image=/distribute/images/gpfe-faq.jpg </p> <div class="headerLine"> - <h1 id="discovery"> + <h2 id="discovery"> Discovery - </h1> + </h2> + - <hr> </div> <p> @@ -211,11 +211,11 @@ page.image=/distribute/images/gpfe-faq.jpg </p> <div class="headerLine"> - <h1 id="app-review-process"> + <h2 id="app-review-process"> App Review Process - </h1> + </h2> + - <hr> </div> <p> @@ -255,11 +255,11 @@ page.image=/distribute/images/gpfe-faq.jpg </p> <div class="headerLine"> - <h1 id="app-features"> + <h2 id="app-features"> App Features - </h1> + </h2> + - <hr> </div> <p> @@ -310,11 +310,11 @@ page.image=/distribute/images/gpfe-faq.jpg </p> <div class="headerLine"> - <h1 id="marketing-and-roi"> + <h2 id="marketing-and-roi"> Marketing and ROI - </h1> + </h2> + - <hr> </div> <p> @@ -364,11 +364,11 @@ page.image=/distribute/images/gpfe-faq.jpg </p> <div class="headerLine"> - <h1 id="devices"> + <h2 id="devices"> Devices - </h1> + </h2> + - <hr> </div> <p> @@ -393,11 +393,11 @@ page.image=/distribute/images/gpfe-faq.jpg </p> <div class="headerLine"> - <h1 id="accounts"> + <h2 id="accounts"> Accounts - </h1> + </h2> + - <hr> </div> <p> @@ -423,7 +423,7 @@ page.image=/distribute/images/gpfe-faq.jpg schools in the program use Google Accounts and Google Apps for Education, offering Google Single Sign-on is ideal. </p> -<div class="headerLine"><h1 id="related-resources">Related Resources</h1><hr></div> +<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/toolsreference/gpfefaq" diff --git a/docs/html/distribute/googleplay/edu/start.jd b/docs/html/distribute/googleplay/edu/start.jd index 260ae85..4886b5a 100644 --- a/docs/html/distribute/googleplay/edu/start.jd +++ b/docs/html/distribute/googleplay/edu/start.jd @@ -32,12 +32,12 @@ page.metaDescription=Join Google Play for Education in just a few simple steps. "border:1px solid #ddd;padding:0px;width:100%;"> </div> -<div class="headerLine clearfloat"> - <h1 id="register"> +<div class="headerLine"> + <h2 id="register"> Register for a Publisher Account - </h1> + </h2> + - <hr> </div> <p> @@ -49,11 +49,11 @@ page.metaDescription=Join Google Play for Education in just a few simple steps. </p> <div class="headerLine"> - <h1 id="prepare"> + <h2 id="prepare"> Prepare Your Apps - </h1> + </h2> + - <hr> </div> <div class="figure-right"> @@ -138,11 +138,11 @@ page.metaDescription=Join Google Play for Education in just a few simple steps. </p> <div class="headerLine"> - <h1 id="publish"> + <h2 id="publish"> Publish Your Apps - </h1> + </h2> + - <hr> </div> <p> @@ -298,7 +298,7 @@ page.metaDescription=Join Google Play for Education in just a few simple steps. </li> </ul> <div class="headerLine"> -<h1 id="related-resources">Related Resources</h1><hr> +<h2 id="related-resources">Related Resources</h2> </div> <div class="dynamic-grid"> diff --git a/docs/html/distribute/googleplay/start.jd b/docs/html/distribute/googleplay/start.jd index 6dc397b..2ba5880 100644 --- a/docs/html/distribute/googleplay/start.jd +++ b/docs/html/distribute/googleplay/start.jd @@ -33,11 +33,11 @@ page.image=/distribute/images/getting-started.jpg </p> <div class="headerLine"> - <h1> + <h2> Register for a Publisher Account - </h1> + </h2> + - <hr> </div> <div class="sidebox-wrapper" style="float:right;"> @@ -92,11 +92,11 @@ page.image=/distribute/images/getting-started.jpg </ol> <div class="headerLine"> - <h1 id="merchant-account"> + <h2 id="merchant-account"> Set Up a Google Wallet Merchant Account - </h1> + </h2> + - <hr> </div> <div class="figure" style="width:200px;"> @@ -135,11 +135,11 @@ page.image=/distribute/images/getting-started.jpg </p> <div class="headerLine"> - <h1> + <h2> Explore the Developer Console - </h1> + </h2> + - <hr> </div> <p> @@ -153,7 +153,7 @@ page.image=/distribute/images/getting-started.jpg </div> <div class="headerLine"> -<h1 id="related-resources">Related Resources</h1><hr /> +<h2 id="related-resources">Related Resources</h2><hr /> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/monetize/ads.jd b/docs/html/distribute/monetize/ads.jd index 40120c3..bcb1e52 100644 --- a/docs/html/distribute/monetize/ads.jd +++ b/docs/html/distribute/monetize/ads.jd @@ -104,7 +104,7 @@ page.image=/distribute/images/advertising.png DoubleClick for Publishers Small Business</a>. </p> -<div class="headerLine"><h1 id="related-resources">Related resources</h1><hr></div> +<div class="headerLine"><h2 id="related-resources">Related resources</h2></div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/monetize/advertising" diff --git a/docs/html/distribute/monetize/ecommerce.jd b/docs/html/distribute/monetize/ecommerce.jd index b3f790d..65e2b20 100644 --- a/docs/html/distribute/monetize/ecommerce.jd +++ b/docs/html/distribute/monetize/ecommerce.jd @@ -44,12 +44,14 @@ page.tags="monetizing", "physical goods", "wallet" Account</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<p style="clear:both"> +</p> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/monetize/ecommerce" diff --git a/docs/html/distribute/monetize/freemium.jd b/docs/html/distribute/monetize/freemium.jd index ec86d19..0d33054 100644 --- a/docs/html/distribute/monetize/freemium.jd +++ b/docs/html/distribute/monetize/freemium.jd @@ -66,11 +66,11 @@ page.tags="in-app", "billing", "iap", "monetizing" </p> <div class="headerLine"> - <h1 id="related-resources"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/monetize/payments.jd b/docs/html/distribute/monetize/payments.jd index 37b4d44..55c289f 100644 --- a/docs/html/distribute/monetize/payments.jd +++ b/docs/html/distribute/monetize/payments.jd @@ -17,11 +17,11 @@ page.tags="google play", "payments", "gift card" </p> <div class="headerLine"> - <h1 id="dcb"> + <h2 id="dcb"> Direct Carrier Billing - </h1> + </h2> + - <hr> </div> <p> @@ -33,10 +33,10 @@ page.tags="google play", "payments", "gift card" </p> <div class="headerLine"> - <h1 id="credit"> + <h2 id="credit"> Credit Cards - </h1> - <hr> + </h2> + </div> <p> @@ -47,12 +47,12 @@ page.tags="google play", "payments", "gift card" setup process. </p> -<div class="headerLine clearfloat"> - <h1 id="gift-cards"> +<div class="headerLine"> + <h2 id="gift-cards"> Google Play Gift Cards - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -67,12 +67,14 @@ page.tags="google play", "payments", "gift card" "_android">here</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="balance"> +<p style="clear:both"> +</p> +<div class="headerLine"> + <h2 id="balance"> Google Play Balance - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -94,7 +96,9 @@ page.tags="google play", "payments", "gift card" network, and other factors. </p> -<div class="headerLine clearfloat"><h1 id="related-resources">Related Resources</h1><hr></div> +<p style="clear:both"> +</p> +<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/monetize/paymentmethods" diff --git a/docs/html/distribute/monetize/premium.jd b/docs/html/distribute/monetize/premium.jd index b66cd03..c8823d1 100644 --- a/docs/html/distribute/monetize/premium.jd +++ b/docs/html/distribute/monetize/premium.jd @@ -35,11 +35,13 @@ page.tags="monetizing", "paid" <a href="{@docRoot}distribute/monetize/ads.html">advertising</a> models. </p> -<div class="headerLine clearfloat"> - <h1> +<p style="clear:both"> +</p> +<div class="headerLine"> + <h2> Related Resources - </h1> - <hr> + </h2> + </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/monetize/subscriptions.jd b/docs/html/distribute/monetize/subscriptions.jd index a838e30..6a4d9b1 100644 --- a/docs/html/distribute/monetize/subscriptions.jd +++ b/docs/html/distribute/monetize/subscriptions.jd @@ -61,7 +61,9 @@ page.tags="in-app", "iap", "monetizing", "free", "trials" </p> </div> -<div class="headerLine clearfloat"><h1 id="related-resources">Related Resources</h1><hr></div> +<p style="clear:both"> +</p> +<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/monetize/subscriptions" diff --git a/docs/html/distribute/tools/launch-checklist.jd b/docs/html/distribute/tools/launch-checklist.jd index 3b0dd55..3f6b1a6 100644 --- a/docs/html/distribute/tools/launch-checklist.jd +++ b/docs/html/distribute/tools/launch-checklist.jd @@ -62,11 +62,11 @@ src="{@docRoot}distribute/images/launch-checklist.jpg"></div> </p> <div class="headerLine"> - <h1 id="understand-publishing"> + <h2 id="understand-publishing"> 1. Understand the Publishing Process - </h1> + </h2> + - <hr> </div> <p> @@ -95,12 +95,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="understand-policies"> +<div class="headerLine"> + <h2 id="understand-policies"> 2. Understand Google Play Policies and Agreements - </h1> + </h2> + - <hr> </div> <p> @@ -119,12 +119,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="6x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="test-quality"> +<div class="headerLine"> + <h2 id="test-quality"> 3. Test for Quality - </h1> + </h2> + - <hr> </div> <p> @@ -163,12 +163,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="6x3,6x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="determine-rating"> +<div class="headerLine"> + <h2 id="determine-rating"> 4. Determine your App’s Content Rating - </h1> + </h2> + - <hr> </div> <p> @@ -221,12 +221,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="9x3,6x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="determine-country"> +<div class="headerLine"> + <h2 id="determine-country"> 5. Determine Country Distribution - </h1> + </h2> + - <hr> </div> <p> @@ -289,12 +289,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="confirm-size"> +<div class="headerLine"> + <h2 id="confirm-size"> 6. Confirm the App's Overall Size - </h1> + </h2> + - <hr> </div> <p> @@ -344,12 +344,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="confirm-platform"> +<div class="headerLine"> + <h2 id="confirm-platform"> 7. Confirm the App's Platform and Screen Compatibility Ranges - </h1> + </h2> + - <hr> </div> <p> @@ -396,12 +396,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="6x3,6x3,6x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="decide-price"> +<div class="headerLine"> + <h2 id="decide-price"> 8. Decide Whether your App will be Free or Priced - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -459,12 +459,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="consider-billing"> +<div class="headerLine"> + <h2 id="consider-billing"> 9. Consider using In-app Billing - </h1> + </h2> + - <hr> </div> <p> @@ -495,12 +495,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="set-prices"> +<div class="headerLine"> + <h2 id="set-prices"> 10. Set Prices for your Products - </h1> + </h2> + - <hr> </div> <p> @@ -526,12 +526,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="9x3,9x3,9x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="start-localization"> +<div class="headerLine"> + <h2 id="start-localization"> 11. Start Localization - </h1> + </h2> + - <hr> </div> <p> @@ -609,12 +609,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="prepare-graphics"> +<div class="headerLine"> + <h2 id="prepare-graphics"> 12. Prepare Promotional Graphics, Screenshots, and Videos - </h1> + </h2> + - <hr> </div> <p> @@ -668,12 +668,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="build-upload"> +<div class="headerLine"> + <h2 id="build-upload"> 13. Build and Upload the Release-ready APK - </h1> + </h2> + - <hr> </div> <p> @@ -709,12 +709,12 @@ data-maxresults="6"> data-cardSizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxResults="6"></div>--> -<div class="headerLine clearfloat"> - <h1 id="plan-beta"> +<div class="headerLine"> + <h2 id="plan-beta"> 14. Plan a Beta Release - </h1> + </h2> + - <hr> </div> <div class="sidebox-wrapper" style="float:right;"> @@ -765,11 +765,11 @@ See how you can facilitate testing with Google Play.</td> </table> --> <div class="headerLine"> - <h1 id="complete-details"> + <h2 id="complete-details"> 15. Complete the Apps’ Store Listing - </h1> + </h2> + - <hr> </div> <p> @@ -825,12 +825,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="use-badges"> +<div class="headerLine"> + <h2 id="use-badges"> 16. Use Google Play Badges and Links in your Promotional Campaigns - </h1> + </h2> + - <hr> </div> <p> @@ -862,12 +862,12 @@ data-maxresults="6"> "-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="final-checks"> +<div class="headerLine"> + <h2 id="final-checks"> 17. Final Checks and Publishing - </h1> + </h2> + - <hr> </div> <p> @@ -969,12 +969,12 @@ data-sortorder="-timestamp" data-cardsizes="6x3,6x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="support-users"> +<div class="headerLine"> + <h2 id="support-users"> 18. Support Users after Launch - </h1> + </h2> + - <hr> </div> <p> diff --git a/docs/html/distribute/tools/localization-checklist.jd b/docs/html/distribute/tools/localization-checklist.jd index 7a638ed..569ed02 100644 --- a/docs/html/distribute/tools/localization-checklist.jd +++ b/docs/html/distribute/tools/localization-checklist.jd @@ -41,11 +41,11 @@ page.image=/distribute/images/localization-checklist.jpg </p> <div class="headerLine"> - <h1 id="identify-languages"> + <h2 id="identify-languages"> 1. Identify target languages and locales - </h1> + </h2> + - <hr> </div> <p> @@ -93,12 +93,12 @@ page.image=/distribute/images/localization-checklist.jpg data-sortorder="-timestamp" data-cardsizes="9x3," data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="design"> +<div class="headerLine"> + <h2 id="design"> 2. Design for localization - </h1> + </h2> + - <hr> </div> <p> @@ -233,12 +233,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3," data-maxresults="6"> data-cardsizes="9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="manage-strings"> +<div class="headerLine"> + <h2 id="manage-strings"> 3. Manage strings for localization - </h1> + </h2> + - <hr> </div> <p> @@ -408,12 +408,12 @@ data-cardsizes="9x3" data-maxresults="6"> data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="translate-strings"> +<div class="headerLine"> + <h2 id="translate-strings"> 4. Translate UI strings and other resources - </h1> + </h2> + - <hr> </div> <p> @@ -579,12 +579,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6"> data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="test"> +<div class="headerLine"> + <h2 id="test"> 5. Test your localized app - </h1> + </h2> + - <hr> </div> <p> @@ -699,12 +699,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3" data-maxresults="6"> data-maxResults="6"></div> --> </p> -<div class="headerLine clearfloat"> - <h1 id="prepare-launch"> +<div class="headerLine"> + <h2 id="prepare-launch"> 6. Prepare for international launch - </h1> + </h2> + - <hr> </div> <p> @@ -897,12 +897,12 @@ data-sortorder="-timestamp" data-cardsizes="9x3,9x3,6x3,9x3,9x3,9x3" data-maxresults="6"> </div> -<div class="headerLine clearfloat"> - <h1 id="support-users"> +<div class="headerLine"> + <h2 id="support-users"> 7. Support international users after launch - </h1> + </h2> + - <hr> </div> <p> diff --git a/docs/html/distribute/tools/open-distribution.jd b/docs/html/distribute/tools/open-distribution.jd index f804af2..e28102d 100644 --- a/docs/html/distribute/tools/open-distribution.jd +++ b/docs/html/distribute/tools/open-distribution.jd @@ -26,11 +26,11 @@ page.image=/distribute/images/alt-distribution.jpg </p> <div class="headerLine"> - <h1> + <h2> Distributing Through an App Marketplace - </h1> + </h2> + - <hr> </div> <p> @@ -55,11 +55,11 @@ page.image=/distribute/images/alt-distribution.jpg </p> <div class="headerLine"> - <h1> + <h2> Distributing Your Apps by Email - </h1> + </h2> + - <hr> </div> <div class="figure" style="width:300px;"> @@ -95,11 +95,11 @@ page.image=/distribute/images/alt-distribution.jpg </p> <div class="headerLine"> - <h1> + <h2> Distributing Through a Website - </h1> + </h2> + - <hr> </div> <p> @@ -119,12 +119,12 @@ page.image=/distribute/images/alt-distribution.jpg sources</a>. </p> -<div class="headerLine clearfloat"> - <h1> +<div class="headerLine"> + <h2> User Opt-In for Apps from Unknown Sources - </h1> + </h2> + - <hr> </div> <div class="figure" style="width:325px;"> diff --git a/docs/html/distribute/tools/promote/badge-files.jd b/docs/html/distribute/tools/promote/badge-files.jd index cce3632..b481802 100644 --- a/docs/html/distribute/tools/promote/badge-files.jd +++ b/docs/html/distribute/tools/promote/badge-files.jd @@ -12,7 +12,7 @@ table tr td {border:0} <p>The following links provide the Adobe® Illustrator® (.ai) file for the two Google Play badges.</p> -<hr> + <img src="{@docRoot}images/brand/en_generic_rgb_wo_60.png" alt="Get It On Google Play"> <div style="clear:left"> </div> @@ -137,7 +137,7 @@ two Google Play badges.</p> -<hr> + <img src="{@docRoot}images/brand/en_app_rgb_wo_60.png" alt="Android App On Google Play"> <div style="clear:left"> </div> diff --git a/docs/html/distribute/tools/promote/brand.jd b/docs/html/distribute/tools/promote/brand.jd index 2116c0f..9b9f9a3 100644 --- a/docs/html/distribute/tools/promote/brand.jd +++ b/docs/html/distribute/tools/promote/brand.jd @@ -9,6 +9,11 @@ page.tags="brand, bugdroid, assets" promotional materials. You can use the icons and other assets on this page provided that you follow the guidelines.</p> +<p>Use of the Android or Google Play brands must be reviewed by the Android +Partner Marketing team. Use the <a +href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android and Google Play Brand Permissions Inquiry form</a> to submit your +marketing for review.</p> + <h2 id="brand-android">Android</h2> <p>The following are guidelines for the Android brand @@ -29,10 +34,12 @@ provided that you follow the guidelines.</p> <li><span style="color:red">Incorrect</span>: "Android MediaPlayer"</li> <li><span style="color:green">Correct</span>: "MediaPlayer for Android"</li> </ul> - <p>If used with your logo, "for Android" needs to be smaller in size than your logo. + <p>If used with your logo, "for Android" should be no larger than 90% of your logo’s size. First instance of this use should be followed by a TM symbol, "for Android™".</p> </li> - <li>Android may be used as a descriptor, as long as it is followed by a proper generic term. + <li>Android may be used as a descriptor, as long as it is followed by a + proper generic term. (Think of "Android" as a term used in place of + "the Android platform.") <ul> <li><span style="color:red">Incorrect</span>: "Android MediaPlayer" or "Android XYZ app"</li> <li><span style="color:green">Correct</span>: "Android features" or "Android applications"</li> @@ -62,14 +69,14 @@ provided that you follow the guidelines.</p> <p>When using the Android Robot or any modification of it, proper attribution is required under the terms of the <a href="http://creativecommons.org/licenses/by/3.0/">Creative -Commons Attribution</a> license:</p> + Commons Attribution 3.0</a> license:</p> <blockquote><em>The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.</em></blockquote> - <p>You may not file trademark applications incorporating the Android robot logo or -derivatives thereof. We want to ensure that the Android robot remains available -for all to use.</p> + <p>You may not file trademark applications incorporating the Android robot + logo or derivatives thereof within your company logo or business name. We + want to ensure that the Android robot remains available for all to use.</p> <h4 style="clear:right">Android logo</h4> @@ -78,11 +85,9 @@ for all to use.</p> <img alt="" src="{@docRoot}images/brand/android_logo_no.png"> </div> -<p>The Android logo may not be used. Nor can this be used with the Android robot.</p> -<p>The custom typeface may not be used.</p> - - +<p>The Android logo may not be used.</p> +<p>The custom typeface may not be used.</p> <h2 id="brand-google_play">Google Play</h2> @@ -98,7 +103,7 @@ in text.</p> <p>When referring to the mobile experience, use "Google Play" unless the text is clearly instructional for the user. For example, a marketing headline might read "Download our games on Google Play™," but instructional text would read "Download our games using the Google -Play™ Store app." +Play™ store app." <p>Any use of the Google Play name or icon needs to include this attribution in your communication:</p> @@ -111,16 +116,16 @@ Play™ Store app." <p style="text-align:center"> <a href="{@docRoot}images/brand/Google_Play_Store_48.png">48x48</a> | <a href="{@docRoot}images/brand/Google_Play_Store_96.png">96x96</a><br> - <a href="{@docRoot}downloads/brand/Google_Play_Store.ai">Illustrator (.ai)</a> + <a href="{@docRoot}images/brand/Google_Play_Store_600.png">600x576</a> </p> </div> -<h4>Google Play Store icon</h4> +<h4>Google Play store icon</h4> -<p>You may use the Google Play Store icon, but you may not modify it.</p> +<p>You may use the Google Play store icon, but you may not modify it.</p> -<p>As mentioned above, when referring to the Google Play Store app in copy, use the full name: -"Google Play Store." However, when labeling the Google Play Store icon directly, it's OK to use +<p>As mentioned above, when referring to the Google Play store app in copy, use the full name: +"Google Play store." However, when labeling the Google Play store icon directly, it's OK to use "Play Store" alone to accurately reflect the icon label as it appears on a device.</p> @@ -140,9 +145,14 @@ Play™ Store app." <a href="{@docRoot}images/brand/en_generic_rgb_wo_60.png">172x60</a></p> </div> - <p>The "Get it on Google Play" and "Android App on Google Play" logos are badges that you - can use on your website and promotional materials, to point to your products on Google - Play.</p> + <p>The "Get it on Google Play" and "Android App on Google Play" logos are + badges that you can use on your website and promotional materials, to point + to your products on Google Play. Additional Google Play badge formats and + badges for music, books, magazines, movies, and TV shows are also available. + Use the <a + href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android + and Google Play Brand Permissions Inquiry form</a> to request + those badges.</p> <ul> <li>Don't modify the color, proportions, spacing, or any other aspect of the badge image. @@ -163,7 +173,7 @@ Play™ Store app." <p>To quickly create a badge that links to your apps on Google Play, use the <a - href="{@docRoot}distribute/tools/promote/badges.html">Googe Play badge generator</a> + href="{@docRoot}distribute/tools/promote/badges.html">Google Play badge generator</a> (provides the badge in over 40 languages).</p> <p>To create your own size, download an Adobe® Illustrator® (.ai) file for the @@ -171,25 +181,11 @@ Play™ Store app." badge in over 40 languages</a>.</p> <p>For details on all the ways that you can link to your product details page in Google Play, - see <a href="{@docRoot}distribute/tools/promote/linking.html">Linking to your products</a></p> - - - -<h2 id="Questions">Questions</h2> - -<p>To view our full guidelines or for any further brand usage questions, please contact our -Android Partner Marketing team:</p> -<ul> - <li>For North and South America, please contact <a - href="mailto:android-brand-approvals@google.com?Subject=Brand%20Approval%20Questions" - >android-brand-approvals@google.com</a></li> - - <li>For Europe and Emerging Markets, please contact <a - href="mailto:emea-android-brand@google.com?Subject=Brand%20Approval%20Questions" - >emea-android-brand@google.com</a></li> + see <a href="{@docRoot}distribute/tools/promote/linking.html">Linking to your products</a>.</p> - <li>For Asia and Pacific-America, please contact <a - href="mailto:apac-android-brand-approvals@google.com?Subject=Brand%20Approval%20Questions" - >apac-android-brand-approvals@google.com</a></li> -</ul> +<h2 id="Marketing_Review">Marketing Reviews and Brand Inquiries</h2> +<p>Use the <a +href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android +and Google Play Brand Permissions Inquiry form</a> to submit any marketing +reviews or brand inquires. Typical response time is at least one week.</p> diff --git a/docs/html/distribute/tools/promote/device-art.jd b/docs/html/distribute/tools/promote/device-art.jd index b0b5f84..a204ea1 100644 --- a/docs/html/distribute/tools/promote/device-art.jd +++ b/docs/html/distribute/tools/promote/device-art.jd @@ -12,7 +12,7 @@ Xnonavpage=true <p class="note"><strong>Note</strong>: Do <em>not</em> use graphics created here in your 1024x500 feature image or screenshots for your Google Play app listing.</p> -<hr> + <div class="supported-browser"> @@ -28,7 +28,7 @@ feature image or screenshots for your Google Play app listing.</p> </div> </div> -<hr> + <div class="layout-content-row"> <div class="layout-content-col span-3"> diff --git a/docs/html/distribute/users/build-buzz.jd b/docs/html/distribute/users/build-buzz.jd index b76498e..412589f 100644 --- a/docs/html/distribute/users/build-buzz.jd +++ b/docs/html/distribute/users/build-buzz.jd @@ -65,11 +65,11 @@ page.tags="users, growth, promotion" </p> <div class="headerLine"> - <h1 id="link-to-your-apps"> + <h2 id="link-to-your-apps"> Link to Your Apps in Google Play - </h1> + </h2> + - <hr> </div> <p> @@ -101,12 +101,12 @@ page.tags="users, growth, promotion" </li> </ul> -<div class="headerLine clearfloat"> - <h1 id="use-the-google-play-badge"> +<div class="headerLine"> + <h2 id="use-the-google-play-badge"> Use the Google Play Badge - </h1> + </h2> + - <hr> </div> <div class="figure" style="margin:0 3em;"> @@ -126,12 +126,12 @@ page.tags="users, growth, promotion" also easy to make and available in multiple languages. </p> -<div class="headerLine clearfloat"> - <h1 id="cross-promote-from-your-other-apps"> +<div class="headerLine"> + <h2 id="cross-promote-from-your-other-apps"> Cross-Promote from Your Other Apps - </h1> + </h2> + - <hr> </div> <div class="figure-right"> @@ -158,11 +158,11 @@ page.tags="users, growth, promotion" </p> <div class="headerLine"> - <h1 id="hold-a-contest"> + <h2 id="hold-a-contest"> Hold a Contest - </h1> + </h2> + - <hr> </div> <p> @@ -180,11 +180,11 @@ page.tags="users, growth, promotion" </p> <div class="headerLine"> - <h1 id="leverage-pr"> + <h2 id="leverage-pr"> Leverage PR - </h1> + </h2> + - <hr> </div> <p> @@ -196,11 +196,11 @@ page.tags="users, growth, promotion" </p> <div class="headerLine"> - <h1 id="use-social-media"> + <h2 id="use-social-media"> Use Social Media - </h1> + </h2> + - <hr> </div> <p> @@ -220,12 +220,12 @@ page.tags="users, growth, promotion" review your apps. A review on the right blog is a great promotion. </p> -<div class="headerLine clearfloat"> - <h1 id="publish-youtube-videos"> +<div class="headerLine"> + <h2 id="publish-youtube-videos"> Publish YouTube Videos - </h1> + </h2> + - <hr> </div> <div class="center-img" style="padding-top:1em;"> @@ -240,11 +240,11 @@ page.tags="users, growth, promotion" </p> <div class="headerLine"> - <h1 id="advertise"> + <h2 id="advertise"> Advertise - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -262,12 +262,12 @@ page.tags="users, growth, promotion" Sign up for an AdMob account</a> to get started. </p> -<div class="headerLine clearfloat"> - <h1 id="maximize-your-marketing-spend"> +<div class="headerLine"> + <h2 id="maximize-your-marketing-spend"> Maximize your Marketing Spend - </h1> + </h2> + - <hr> </div> <div class="figure" style="margin: 0 3em;"> @@ -285,12 +285,12 @@ page.tags="users, growth, promotion" platforms simultaneously helps you maximize your return on investment. </p> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> + </h2> + - <hr> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/users/build-community.jd b/docs/html/distribute/users/build-community.jd index 1623939..5cdabea 100644 --- a/docs/html/distribute/users/build-community.jd +++ b/docs/html/distribute/users/build-community.jd @@ -43,11 +43,11 @@ page.tags="users, growth, community" </p> <div class="headerLine"> - <h1 id="starting-your-community"> + <h2 id="starting-your-community"> Starting Your Community - </h1> + </h2> + - <hr> </div> <p> @@ -116,11 +116,11 @@ page.tags="users, growth, community" </p> <div class="headerLine"> - <h1 id="tools-to-build-your-community"> + <h2 id="tools-to-build-your-community"> Tools to Build Your Community - </h1> + </h2> + - <hr> </div> <p> @@ -158,11 +158,11 @@ page.tags="users, growth, community" </p> <div class="headerLine"> - <h1 id="managing-your-community"> + <h2 id="managing-your-community"> Managing Your Community - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -189,11 +189,11 @@ page.tags="users, growth, community" versions or new apps to make them feel special. </p> -<div class="headerLine clearfloat"> - <h1 id="related-resources"> +<div class="headerLine"> + <h2 id="related-resources"> Related Resources - </h1> - <hr> + </h2> + </div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/users/buildcommunity" diff --git a/docs/html/distribute/users/expand-to-new-markets.jd b/docs/html/distribute/users/expand-to-new-markets.jd index cc94a92..df1a0fb 100644 --- a/docs/html/distribute/users/expand-to-new-markets.jd +++ b/docs/html/distribute/users/expand-to-new-markets.jd @@ -77,11 +77,11 @@ page.tags="users, growth, global" </p> <div class="headerLine"> - <h1 id="localize-your-product"> + <h2 id="localize-your-product"> Localize Your Product - </h1> + </h2> + - <hr> </div> <div class="sidebox-wrapper" style="float:right;"> @@ -124,20 +124,16 @@ page.tags="users, growth, global" high-quality professional translations at competitive prices. </p> -<div style="float:left; width:48%; padding:8px;"> - <img src="{@docRoot}images/gp-listing-3.jpg"> -</div> +<img src="{@docRoot}images/gp-listing-3.jpg" style="padding:8px 0"> -<div style="width:48%; padding:8px; float:left"> - <img src="{@docRoot}images/gp-expand-2.jpg"> -</div> +<img src="{@docRoot}images/gp-expand-2.jpg" style="padding:8px 0"> -<div class="headerLine clearfloat"> - <h1 id="testing-and-support"> +<div class="headerLine"> + <h2 id="testing-and-support"> Testing and Support - </h1> + </h2> + - <hr> </div> <p> @@ -162,11 +158,11 @@ page.tags="users, growth, global" </p> <div class="headerLine"> - <h1 id="localize-your-google-play-listing"> + <h2 id="localize-your-google-play-listing"> Localize Your Google Play Store Listing - </h1> + </h2> + - <hr> </div> <div class="sidebox-wrapper" style="float:right;"> @@ -295,11 +291,11 @@ page.tags="users, growth, global" </p> <div class="headerLine"> - <h1 id="marketing"> + <h2 id="marketing"> Marketing - </h1> + </h2> + - <hr> </div> <div class="figure"> @@ -316,7 +312,7 @@ page.tags="users, growth, global" for each language you support. </p> -<div class="headerLine clearfloat"><h1 id="related-resources">Related Resources</h1><hr></div> +<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div> <div class="resource-widget resource-flow-layout col-13" data-query="collection:distribute/getusers/expandnewmarkets" diff --git a/docs/html/distribute/users/know-your-user.jd b/docs/html/distribute/users/know-your-user.jd index fb91a05..1fbcb9c 100644 --- a/docs/html/distribute/users/know-your-user.jd +++ b/docs/html/distribute/users/know-your-user.jd @@ -15,13 +15,9 @@ page.tags="users, growth, global" when they use your app, and how often they return to it. </p> -<div class="headerLine"> - <h1 id="read-ratings-comments"> + <h2 id="read-ratings-comments"> Read Ratings Comments - </h1> - - <hr> -</div> + </h2> <p> The most obvious way to get to know your users is by reading review comments @@ -46,11 +42,11 @@ page.tags="users, growth, global" </div> <div class="headerLine"> - <h1 id="start-community"> + <h2 id="start-community"> Start a Community - </h1> + </h2> + - <hr> </div> <p> @@ -68,11 +64,11 @@ page.tags="users, growth, global" </p> <div class="headerLine"> - <h1 id="create-survey"> + <h2 id="create-survey"> Create a Survey - </h1> + </h2> + - <hr> </div> <p> @@ -92,11 +88,11 @@ page.tags="users, growth, global" </p> <div class="headerLine"> - <h1 id="add-analytics"> + <h2 id="add-analytics"> Add Analytics to your Apps - </h1> + </h2> + - <hr> </div> <p> @@ -121,11 +117,11 @@ page.tags="users, growth, global" </div> <div class="headerLine"> - <h1 id="use-google"> + <h2 id="use-google"> Use Google+ - </h1> + </h2> + - <hr> </div> <p> @@ -142,8 +138,8 @@ page.tags="users, growth, global" <img src="{@docRoot}images/gp-your-user-2.jpg"> </div> -<div class="headerLine clearfloat"> -<h1 id="related-resources">Related Resources</h1><hr> +<div class="headerLine"> +<h2 id="related-resources">Related Resources</h2> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/distribute/users/your-listing.jd b/docs/html/distribute/users/your-listing.jd index cc72fff..f869950 100644 --- a/docs/html/distribute/users/your-listing.jd +++ b/docs/html/distribute/users/your-listing.jd @@ -13,11 +13,11 @@ page.tags="listing, google play, users, growth" </p> <div class="headerLine"> - <h1 id="graphics-imagery"> + <h2 id="graphics-imagery"> Graphics & Imagery Tips - </h1> + </h2> + - <hr> </div> <p> @@ -94,12 +94,12 @@ page.tags="listing, google play, users, growth" Featured-Image Guidelines</a>. </p> -<div class="headerLine clearfloat"> - <h1 id="localization-tips"> +<div class="headerLine"> + <h2 id="localization-tips"> Localization Tips - </h1> + </h2> + - <hr> </div> <div class="sidebox-wrapper" style="float:right;"> @@ -125,16 +125,14 @@ page.tags="listing, google play, users, growth" the growing international audience</a>. </p> -<div style="float:left;"> <img src="{@docRoot}images/gp-listing-3.jpg"> -</div> -<div class="headerLine clearfloat"> - <h1 id="keyword-tips"> +<div class="headerLine"> + <h2 id="keyword-tips"> Keyword Tips - </h1> + </h2> + - <hr> </div> <p> @@ -146,11 +144,11 @@ page.tags="listing, google play, users, growth" </p> <div class="headerLine"> - <h1 id="app-indexing"> + <h2 id="app-indexing"> Sign Up for App Indexing - </h1> + </h2> + - <hr> </div> <p> @@ -164,11 +162,11 @@ page.tags="listing, google play, users, growth" </div> <div class="headerLine"> - <h1 id="education-app"> + <h2 id="education-app"> Education App Tips - </h1> + </h2> + - <hr> </div> <p> @@ -181,7 +179,7 @@ page.tags="listing, google play, users, growth" </p> <div class="headerLine"> -<h1 id="related-resources">Related Resources</h1><hr> +<h2 id="related-resources">Related Resources</h2> </div> <div class="resource-widget resource-flow-layout col-13" diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd index 46ad905..5904b03 100644 --- a/docs/html/google/play/billing/billing_admin.jd +++ b/docs/html/google/play/billing/billing_admin.jd @@ -66,7 +66,8 @@ storing and delivering the digital content that you sell in your applications.</ </p> </div> -<p>You can create a product list for any published application or any draft application that's been +<p>You can create a product list for any published application, or any +application in the alpha or beta channels, that's been uploaded and saved to the Developer Console. However, you must have a Google Wallet merchant account and the application's manifest must include the <code>com.android.vending.BILLING</code> permission. If an application's manifest does not include this permission, you will be able to edit @@ -75,6 +76,13 @@ information about this permission, see <a href="{@docRoot}google/play/billing/billing_integrate.html#billing-permission">Updating Your Application's Manifest</a>.</p> +<p class="note"><strong>Note:</strong> Previously you could test an app by +uploading an unpublished "draft" version. This functionality is no longer +supported; instead, you must publish it to the alpha or beta distribution +channel. For more information, see <a +href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps +are No Longer Supported</a>. + <p>In addition, an application package can have only one product list. If you create a product list for an application, and you use the <a href="{@docRoot}google/play/publishing/multiple-apks.html">multiple APK feature</a> to distribute diff --git a/docs/html/google/play/billing/billing_testing.jd b/docs/html/google/play/billing/billing_testing.jd index df6c657..8a49433 100644 --- a/docs/html/google/play/billing/billing_testing.jd +++ b/docs/html/google/play/billing/billing_testing.jd @@ -8,8 +8,9 @@ parent.link=index.html <h2>In this document</h2> <ol> <li><a href="#testing-purchases">Testing In-app Purchases</a></li> - <li><a href="#billing-testing-static">Testing with static responses</a></li> + <li><a href="#billing-testing-static">Testing with Static Responses</a></li> <li><a href="#billing-testing-real">Setting Up for Test Purchases</a></li> + <li><a href="#draft_apps">Draft Apps are No Longer Supported</a></li> </ol> <h2>See also</h2> <ol> @@ -79,8 +80,7 @@ method).</p> <p>First, upload and publish in-app products that you want testers to be able to purchase. You can upload and publish in-app products in the Developer Console. Note that you can upload and publish your in-app items before you publish the -APK itself. For example, you can publish your in-app items while your APK is -still a draft. </p> +APK itself.</p> <p>Next, create license test accounts for authorized users. In the Developer Console, go to <strong>Settings</strong> > <strong>Account details</strong>, @@ -149,11 +149,12 @@ license accounts in your alpha and beta distribution groups, those users will only be able to make test purchases. </p> -<h2 id="billing-testing-static">Testing with static responses</h2> +<h2 id="billing-testing-static">Testing with Static Responses</h2> <p>We recommend that you first test your In-app Billing implementation using static responses from Google Play. This enables you to verify that your application is handling the primary Google -Play responses correctly and that your application is able to verify signatures correctly.</p> +Play responses correctly and that your application is able to verify signatures correctly. You can do this +even if the app hasn't been published yet.</p> <p>To test your implementation with static responses, you make an In-app Billing request using a special item that has a reserved product ID. Each reserved product ID returns a specific static @@ -173,6 +174,12 @@ the Developer Console to perform static response tests with the reserved product install your application on a device, log into the device, and make billing requests using the reserved product IDs.</p> +<p class="note"><strong>Note:</strong> Previously you could test an app by +uploading an unpublished "draft" version. This functionality is no longer +supported. However, you can test your app with static responses even before you +upload it to the Google Play store. For more information, see <a +href="#draft_apps">Draft Apps are No Longer Supported</a>. + <p>There are four reserved product IDs for testing static In-app Billing responses:</p> <ul> @@ -205,67 +212,12 @@ Pricing</a>.</p> </li> </ul> -<p>In some cases, the reserved items may return signed static responses, which lets you test -signature verification in your application. To test signature verification with the special reserved -product IDs, you may need to set up <a -href="{@docRoot}google/play/billing/billing_admin.html#billing-testing-setup">test accounts</a> or -upload your application as a unpublished draft application. Table 1 shows you the conditions under -which static responses are signed.</p> - -<p class="table-caption" id="static-responses-table"><strong>Table 1.</strong> -Conditions under which static responses are signed.</p> - -<table> -<tr> -<th>Application ever been published?</th> -<th>Draft application uploaded and unpublished?</th> -<th>User who is running the application</th> -<th>Static response signature</th> -</tr> - -<tr> -<td>No</td> -<td>No</td> -<td>Any</td> -<td>Unsigned</td> -</tr> - -<tr> -<td>No</td> -<td>No</td> -<td>Developer</td> -<td>Signed</td> -</tr> - -<tr> -<td>Yes</td> -<td>No</td> -<td>Any</td> -<td>Unsigned</td> -</tr> - -<tr> -<td>Yes</td> -<td>No</td> -<td>Developer</td> -<td>Signed</td> -</tr> - -<tr> -<td>Yes</td> -<td>No</td> -<td>Test account</td> -<td>Signed</td> -</tr> - -<tr> -<td>Yes</td> -<td>Yes</td> -<td>Any</td> -<td>Signed</td> -</tr> - -</table> +<p>In some cases, the reserved items may return signed static responses, which +lets you test signature verification in your application. The reserved items +only return signed responses if the user running the application has a <a +href="{@docRoot}distribute/googleplay/start.html">developer</a> or <a +href="{@docRoot}google/play/billing/billing_admin.html#billing-testing-setup">test +account.</a> <p>To make an In-app Billing request with a reserved product ID, you simply construct a normal <code>REQUEST_PURCHASE</code> request, but instead of using a real product ID from your @@ -310,9 +262,11 @@ purchases. Testing real in-app purchases enables you to test the end-to-end In-a experience, including the actual purchases from Google Play and the actual checkout flow that users will experience in your application.</p> -<p class="note"><strong>Note</strong>: You do not need to publish your application to do end-to-end -testing. You only need to upload your application as a draft application to perform end-to-end -testing.</p> +<p class="note"><strong>Note:</strong> You can do end-to-end testing of your app + by publishing it to an <a + href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha + distribution channel</a>. This allows you to publish the app to the Google + Play store, but limit its availability to just the testers you designate. </p> <p>To test your In-app Billing implementation with actual in-app purchases, you will need to register at least one test account on the Google Play Developer Console. You cannot use your @@ -327,14 +281,16 @@ application does not need to be published, but the item does need to be publishe <p>To test your In-app Billing implementation with actual purchases, follow these steps:</p> <ol> - <li><strong>Upload your application as a draft application to the Developer Console.</strong> - <p>You do not need to publish your application to perform end-to-end testing with real product - IDs; you only need to upload your application as a draft application. However, you must sign - your application with your release key before you upload it as a draft application. Also, the - version number of the uploaded application must match the version number of the application you - load to your device for testing. To learn how to upload an application to Google Play, see - <a href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=113469">Uploading - applications</a>.</p> + <li><strong>Upload your application to the <a + href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha + distribution channel</a> with the Developer Console.</strong> + + <p class="note"><strong>Note:</strong> Previously you could test an app by + uploading an unpublished "draft" version. This functionality is no longer + supported; instead, you must publish it to the alpha or beta distribution + channel. For more information, see <a href="#draft_apps">Draft Apps are No + Longer Supported</a>. + </li> <li><strong>Add items to the application's product list.</strong> <p>Make sure that you publish the items (the application can remain unpublished). See <a @@ -370,3 +326,24 @@ href="{@docRoot}tools/publishing/app-signing.html">signing</a>, and <a href="{@docRoot}distribute/tools/launch-checklist.html">publishing on Google Play</a>. </p> +<h2 id="draft_apps">Draft Apps are No Longer Supported</h2> + +<p>Previously, you could publish a "draft" version of your app for testing. This +functionality is no longer supported. Instead, there are two ways you can test +how a pre-release app functions on the Google Play store:</p> + +<ul> + + <li>You can publish an app to the <a + href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha + or beta distribution channels</a>. This makes the app available on the Google + Play store, but only to the testers you put on a "whitelist".</li> + + <li>In a few cases, you can test Google Play functionality with an unpublished + app. For example, you can test an unpublished app's in-app billing support by + using <a + href="{@docRoot}google/play/billing/billing_testing.html#billing-testing-static">static + responses</a>, special reserved product IDs that always return a specific + result (like "purchased" or "refunded").</li> + +</ul> diff --git a/docs/html/google/play/billing/v2/billing_integrate.jd b/docs/html/google/play/billing/v2/billing_integrate.jd index ca41e0b..5eb17d5 100644 --- a/docs/html/google/play/billing/v2/billing_integrate.jd +++ b/docs/html/google/play/billing/v2/billing_integrate.jd @@ -208,6 +208,14 @@ following:</p> a draft to the Google Play Developer Console. You also need to create a product list for the in-app items that are available for purchase in the sample application. The following instructions show you how to do this.</p> + +<p class="caution"><strong>Caution:</strong> Draft applications are no longer +supported. To test an application, publish it in the <a +href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">alpha +or beta channels</a>. For more information, see <a +href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps +are No Longer Supported</a>.</p> + <ol> <li><strong>Upload the release version of the sample application to Google Play.</strong> <p>Do not publish the sample application; leave it as an unpublished draft application. The @@ -928,10 +936,12 @@ public class BillingReceiver extends BroadcastReceiver { // Intent actions that we receive in the BillingReceiver from Google Play. // These are defined by Google Play and cannot be changed. // The sample application defines these in the Consts.java file. - public static final String ACTION_NOTIFY = "com.android.vending.billing.IN_APP_NOTIFY"; - public static final String ACTION_RESPONSE_CODE = "com.android.vending.billing.RESPONSE_CODE"; + public static final String ACTION_NOTIFY = + "com.android.vending.billing.IN_APP_NOTIFY"; + public static final String ACTION_RESPONSE_CODE = + "com.android.vending.billing.RESPONSE_CODE"; public static final String ACTION_PURCHASE_STATE_CHANGED = - "com.android.vending.billing.PURCHASE_STATE_CHANGED"; + "com.android.vending.billing.PURCHASE_STATE_CHANGED"; // The intent extras that are passed in an intent from Google Play. // These are defined by Google Play and cannot be changed. @@ -962,7 +972,8 @@ public class BillingReceiver extends BroadcastReceiver { Log.w(TAG, "unexpected action: " + action); } } - // Perform other processing here, such as forwarding intent messages to your local service. + // Perform other processing here, such as forwarding intent messages + // to your local service. } </pre> diff --git a/docs/html/google/play/expansion-files.jd b/docs/html/google/play/expansion-files.jd index e90f8fa..601ea48 100644 --- a/docs/html/google/play/expansion-files.jd +++ b/docs/html/google/play/expansion-files.jd @@ -527,17 +527,21 @@ are:</p> <!-- Required to download files from Google Play --> <uses-permission android:name="android.permission.INTERNET" /> - <!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) --> + <!-- Required to keep CPU alive while downloading files + (NOT to keep screen awake) --> <uses-permission android:name="android.permission.WAKE_LOCK" /> - <!-- Required to poll the state of the network connection and respond to changes --> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <!-- Required to poll the state of the network connection + and respond to changes --> + <uses-permission + android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Required to check whether Wi-Fi is enabled --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!-- Required to read and write the expansion files on shared storage --> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission + android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest> </pre> @@ -650,8 +654,8 @@ public class SampleAlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { try { - DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, - SampleDownloaderService.class); + DownloaderClientMarshaller.startDownloadServiceIfRequired(context, + intent, SampleDownloaderService.class); } catch (NameNotFoundException e) { e.printStackTrace(); } @@ -693,16 +697,19 @@ versionCode)}</li> <p>For example, the sample app provided in the Apk Expansion package calls the following method in the activity's {@link android.app.Activity#onCreate onCreate()} method to check whether the expansion files already exist on the device:</p> + <pre> boolean expansionFilesDelivered() { for (XAPKFile xf : xAPKS) { - String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase, xf.mFileVersion); + String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase, + xf.mFileVersion); if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false)) return false; } return true; } </pre> + <p>In this case, each {@code XAPKFile} object holds the version number and file size of a known expansion file and a boolean as to whether it's the main expansion file. (See the sample application's {@code SampleDownloaderActivity} class for details.)</p> @@ -740,6 +747,7 @@ the Downloader Library begins the download and you should update your activity U display the download progress (see the next step). If the response <em>is</em> {@code NO_DOWNLOAD_REQUIRED}, then the files are available and your application can start.</p> <p>For example:</p> + <pre> @Override public void onCreate(Bundle savedInstanceState) { @@ -754,11 +762,14 @@ public void onCreate(Bundle savedInstanceState) { notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); // Start the download service (if required) - int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, + int startResult = + DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SampleDownloaderService.class); - // If download has started, initialize this activity to show download progress + // If download has started, initialize this activity to show + // download progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { - // This is where you do set up to display the download progress (next step) + // This is where you do set up to display the download + // progress (next step) ... return; } // If the download wasn't necessary, fall through to start the app @@ -766,6 +777,7 @@ public void onCreate(Bundle savedInstanceState) { startApp(); // Expansion files are available, start the app } </pre> + </li> <li>When the {@code startDownloadServiceIfRequired()} method returns anything <em>other than</em> {@code NO_DOWNLOAD_REQUIRED}, create an instance of {@code IStub} by @@ -783,9 +795,11 @@ android.app.Activity#onCreate onCreate()} method, after {@code startDownloadServ starts the download. </p> <p>For example, in the previous code sample for {@link android.app.Activity#onCreate onCreate()}, you can respond to the {@code startDownloadServiceIfRequired()} result like this:</p> + <pre> // Start the download service (if required) - int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, + int startResult = + DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SampleDownloaderService.class); // If download has started, initialize activity to show progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { @@ -892,7 +906,8 @@ others. By default, this flag is <em>not</em> enabled, so the user must be on Wi expansion files. You might want to provide a user preference to enable downloads over the cellular network. In which case, you can call: <pre> -mRemoteService.setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR); +mRemoteService + .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR); </pre> </dd> </dl> @@ -975,10 +990,12 @@ to both your expansion files:</p> // The shared path to all app expansion files private final static String EXP_PATH = "/Android/obb/"; -static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) { +static String[] getAPKExpansionFiles(Context ctx, int mainVersion, + int patchVersion) { String packageName = ctx.getPackageName(); Vector<String> ret = new Vector<String>(); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + if (Environment.getExternalStorageState() + .equals(Environment.MEDIA_MOUNTED)) { // Build the full path to the app's expansion files File root = Environment.getExternalStorageDirectory(); File expPath = new File(root.toString() + EXP_PATH + packageName); @@ -1102,7 +1119,8 @@ following:</p> <pre> // Get a ZipResourceFile representing a merger of both the main and patch files -ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext, +ZipResourceFile expansionFile = + APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion); // Get an input stream for a known file inside the expansion file ZIPs @@ -1190,28 +1208,18 @@ android.content.Context#getExternalFilesDir getExternalFilesDir()}.</li> opens, it's important that you test this process to be sure your application can successfully query for the URLs, download the files, and save them to the device.</p> -<p>To test your application's implementation of the manual download procedure, you must upload -your application to Google Play as a "draft" to make your expansion files available for -download:</p> - -<ol> - <li>Upload your APK and corresponding expansion files using the Google Play Developer -Console.</li> - <li>Fill in the necessary application details (title, screenshots, etc.). You can come back and -finalize these details before publishing your application. - <p>Click the <strong>Save</strong> button. <em>Do not click Publish.</em> This saves -the application as a draft, such that your application is not published for Google Play users, -but the expansion files are available for you to test the download process.</p></li> - <li>Install the application on your test device using the Eclipse tools or <a -href="{@docRoot}tools/help/adb.html">{@code adb}</a>.</li> - <li>Launch the app.</li> -</ol> - -<p>If everything works as expected, your application should begin downloading the expansion +<p>To test your application's implementation of the manual download procedure, +you can publish it to the alpha or beta channel, so it will only be available to +authorized testers. +If everything works as expected, your application should begin downloading the expansion files as soon as the main activity starts.</p> - - +<p class="note"><strong>Note:</strong> Previously you could test an app by +uploading an unpublished "draft" version. This functionality is no longer +supported; instead, you must publish it to the alpha or beta distribution +channel. For more information, see <a +href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps +are No Longer Supported</a>. <h2 id="Updating">Updating Your Application</h2> diff --git a/docs/html/google/play/licensing/licensing-reference.jd b/docs/html/google/play/licensing/licensing-reference.jd index 7bfa61a..d4ca79a 100644 --- a/docs/html/google/play/licensing/licensing-reference.jd +++ b/docs/html/google/play/licensing/licensing-reference.jd @@ -151,7 +151,8 @@ returned by the Google Play server in a license response.</p> <tr> <td>{@code LICENSED}</td> <td>The application is licensed to the user. The user has purchased the -application or the application only exists as a draft.</td> +application, or is authorized to download and install the alpha or beta version +of the application.</td> <td>Yes</td> <td><code>VT</code>, <code>GT</code>, <code>GR</code></td> <td><em>Allow access according to {@code Policy} constraints.</em></td> @@ -233,16 +234,14 @@ implementation.</p> href="{@docRoot}google/play/licensing/setting-up.html#test-env"> Setting Up The Testing Environment</a>, the response code can be manually overridden for the application developer and any registered test users via the -Google Play Developer Console. -<br/><br/> -Additionally, as noted above, applications that are in draft mode (in other -words, applications that have been uploaded but have <em>never</em> been -published) will return {@code LICENSED} for all users, even if not listed as a test -user. Since the application has never been offered for download, it is assumed -that any users running it must have obtained it from an authorized channel for -testing purposes.</p> - +Google Play Developer Console.</p> +<p class="note"><strong>Note:</strong> Previously you could test an app by +uploading an unpublished "draft" version. This functionality is no longer +supported; instead, you must publish it to the alpha or beta distribution +channel. For more information, see <a +href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps +are No Longer Supported</a>. <h2 id="extras">Server Response Extras</h2> @@ -430,8 +429,8 @@ public boolean allowAccess() { } } else if (mLastResponse == LicenseResponse.RETRY && ts < mLastResponseTime + MILLIS_PER_MINUTE) { - // Only allow access if we are within the retry period or we haven't used up our - // max retries. + // Only allow access if we are within the retry period + // or we haven't used up our max retries. return (ts <= mRetryUntil || mRetryCount <= mMaxRetries); } return false; diff --git a/docs/html/google/play/licensing/overview.jd b/docs/html/google/play/licensing/overview.jd index 4e1a9c9..a2d5379 100644 --- a/docs/html/google/play/licensing/overview.jd +++ b/docs/html/google/play/licensing/overview.jd @@ -38,12 +38,11 @@ the licensing server and receives the result. The Google Play application sends the result to your application, which can allow or disallow further use of the application as needed.</p> -<p class="note"><strong>Note:</strong> If a paid application has been uploaded -to Google Play, but saved only as a draft application (the app is -unpublished), the licensing server considers all users to be licensed users of -the application (because it's not even possible to purchase the app). This -exception is necessary in order for you to perform testing of your licensing -implementation.</p> +<p class="note"><strong>Note:</strong> If a version of an app is in the alpha or +beta channel, all users who are authorized to download and install that app are +considered to be licensed users of the app. For more information, see <a +href="{@docRoot}distribute/googleplay/developer-console.html#alpha-beta">Alpha +and Beta Testing</a>.</p> <div class="figure" style="width:469px"> <img src="{@docRoot}images/licensing_arch.png" alt=""/> @@ -52,6 +51,12 @@ license check through the License Verification Library and the Google Play client, which handles communication with the Google Play server.</p> </div> +<p class="note"><strong>Note:</strong> Previously you could test an app by +uploading an unpublished "draft" version. This functionality is no longer +supported; instead, you must publish it to the alpha or beta distribution +channel. For more information, see <a +href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps +are No Longer Supported</a>. <p>To properly identify the user and determine the license status, the licensing server requires information about the application and user—your application and the Google Play client work diff --git a/docs/html/guide/topics/ui/settings.jd b/docs/html/guide/topics/ui/settings.jd index 1d36430..f454c4e 100644 --- a/docs/html/guide/topics/ui/settings.jd +++ b/docs/html/guide/topics/ui/settings.jd @@ -820,7 +820,8 @@ public class SettingsActivity extends PreferenceActivity public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType"; ... - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { if (key.equals(KEY_PREF_SYNC_CONN)) { Preference connectionPref = findPreference(key); // Set summary to be the user-description for the selected value @@ -863,7 +864,40 @@ protected void onPause() { } </pre> +<p class="caution"><strong>Caution:</strong> When you call {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()}, the preference manager does not +currently store a strong reference to the listener. You must store a strong +reference to the listener, or it will be susceptible to garbage collection. We +recommend you keep a reference to the listener in the instance data of an object +that will exist as long as you need the listener.</p> + +<p>For example, in the following code, the caller does not keep a reference to +the listener. As a result, the listener will be subject to garbage collection, +and it will fail at some indeterminate time in the future:</p> + +<pre> +prefs.registerOnSharedPreferenceChangeListener( + // Bad! The listener is subject to garbage collection! + new SharedPreferences.OnSharedPreferenceChangeListener() { + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + // listener implementation + } +}); +</pre> + +<p>Instead, store a reference to the listener in an instance data field of an +object that will exist as long as the listener is needed:</p> +<pre> +SharedPreferences.OnSharedPreferenceChangeListener listener = + new SharedPreferences.OnSharedPreferenceChangeListener() { + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + // listener implementation + } +}; +prefs.registerOnSharedPreferenceChangeListener(listener); +</pre> <h2 id="NetworkUsage">Managing Network Usage</h2> @@ -1142,13 +1176,15 @@ protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); // Check whether this Preference is persistent (continually saved) if (isPersistent()) { - // No need to save instance state since it's persistent, use superclass state + // No need to save instance state since it's persistent, + // use superclass state return superState; } // Create instance of custom BaseSavedState final SavedState myState = new SavedState(superState); - // Set the state's value with the class member that holds current setting value + // Set the state's value with the class member that holds current + // setting value myState.value = mNewValue; return myState; } diff --git a/docs/html/images/brand/Google_Play_Store_600.png b/docs/html/images/brand/Google_Play_Store_600.png Binary files differnew file mode 100644 index 0000000..f748652 --- /dev/null +++ b/docs/html/images/brand/Google_Play_Store_600.png diff --git a/docs/html/tools/device.jd b/docs/html/tools/device.jd index e9caa44..e748b12 100644 --- a/docs/html/tools/device.jd +++ b/docs/html/tools/device.jd @@ -280,6 +280,10 @@ above.</p> <td><code>0fce</code></td> </tr> <tr> + <td>Sony Mobile Communications</td> + <td><code>0fce</code></td> + </tr> + <tr> <td>Teleepoch</td> <td><code>2340</code></td> </tr> diff --git a/docs/image_sources/brand/Google_Play_Store.ai b/docs/image_sources/brand/Google_Play_Store.ai deleted file mode 100644 index 51f07c6..0000000 --- a/docs/image_sources/brand/Google_Play_Store.ai +++ /dev/null @@ -1,1419 +0,0 @@ -%PDF-1.5
%âãÏÓ
-1 0 obj
<</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R]/Order 6 0 R/RBGroups[]>>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<</Length 44072/Subtype/XML/Type/Metadata>>stream
-<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> -<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.0-c060 61.134777, 2010/02/12-17:32:00 "> - <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> - <rdf:Description rdf:about="" - xmlns:dc="http://purl.org/dc/elements/1.1/"> - <dc:format>application/pdf</dc:format> - <dc:title> - <rdf:Alt> - <rdf:li xml:lang="x-default">ggp_googleplay_apps1</rdf:li> - </rdf:Alt> - </dc:title> - </rdf:Description> - <rdf:Description rdf:about="" - xmlns:xmp="http://ns.adobe.com/xap/1.0/" - xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/"> - <xmp:CreatorTool>Adobe Illustrator CS5</xmp:CreatorTool> - <xmp:CreateDate>2012-06-25T18:52:36-07:00</xmp:CreateDate> - <xmp:ModifyDate>2012-06-25T18:52:36-07:00</xmp:ModifyDate> - <xmp:MetadataDate>2012-06-25T18:52:36-07:00</xmp:MetadataDate> - <xmp:Thumbnails> - <rdf:Alt> - <rdf:li rdf:parseType="Resource"> - <xmpGImg:width>256</xmpGImg:width> - <xmpGImg:height>248</xmpGImg:height> - <xmpGImg:format>JPEG</xmpGImg:format> - <xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgA+AEAAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYqptOoNB8TeA3OKredy32YwB4sf6VxV3+meEf3n+mKurdjqEPyJ/iMVd60i/bjIH
iN/1YqvSVHFQcVX4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVO
WZYxvUnsBuTiqz0XloZSVX+QdfpIxVWREQURQo8BiqjcX9lbU+sXEcRPQOwUn5AnFUG/mXRENDc1
P+SkjD71U4qqR69o8gqLuNf9c+n/AMT44qjIpoZkDxOsiHoyEMPvGKtS20MhqRR/512OKqRaW3qX
q8f8w6j5jFVdHV1BU1BxVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqjP
cBKKu7t9le5xVbEvE83PKQ9T2HsMVQeoa7BasYo1a4uu0Mfb/Wb9nFUIIta1Chnka2hO/pQkp97D
4jiqtbeWNNiFfSBYmpJJqcVRg0iwAoIhiq19E09xvEMVQcnlizVvUtw0Mo6PGzK33gjFVpl1ywr/
AMfkKj7L7PQDs4H664qjrHVre8FF5RzD7UMg4uP6j3GKr3V4nEkJ+D9uL+K/0xVEQzxypyQgj2xV
UxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVSuJhFGW6mmwxVBo2/N/7w9fYeAxV
KrvV57u7bTtNajRtxurkCoQ90T/K8T2+fRVM9M0WC0Sv2nO7MepOKpmAAKDFXYq7FXYq7FXEAihx
VLNR0dJyskbGOZN0ddiDiqHs9Rk9U2t3RblSQrAUDgdx7+IxVEeqIJDKo+Bj+8Udv8r+uKpkjh1D
DoRXFW8VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqU3dzzuWAPwx7U9yMVY75r1+ey
torOyNdSv2EVsB1XkaF/xoPfCFZH5f0SDTNPhgUAug+Nz1ZjuWPuTgVNcVdirsVdirsVdirsVdiq
T+YdMe4spHtqJdx/HBIKVDqar+PXFUv0fWI9T02K6A4s44zR/wArrs6/fhVN9MuACYCSeI+GvhgV
McVdirsVdirsVdirsVdirsVdiriaDFUO1w+5UKVBoSSBv9OKtC6b/I/4Nf64q0btgein35r/AFxV
31x/5V/4NP64q764/wDKv/Bp/XFXfXH/AJV/4NP64qslvZAhoq1/10/5qxVJHnYuS1AxPxU33+Yw
qxqy0jWrvztDql5bUsIGb0SXjpxRT6e3Ku7fFir0Nbs0+yB/sl/rgVv657L/AMEv9cVd9c9l/wCC
X+uKu+ujwH/BL/XFV/rTf76P4f1xV3rTf76P4Yq71pv99H8MVd603++j+GKtC4k/30w+jFXNKzKQ
Y2ofbFWB6VZ6lpuuatBJbSpp88pntpSpEfJjUqD06N+GFU/spZfrkbRqXFCHpuadjgVPhO9P7tvu
xVr60f8Afb/8C2Ku+tH/AH2//AtirvrR/wB9v/wLYq4XJP8Aut/+BOKrvXP8jf8AAnFWxOD1Uj5g
jFVQYq7FXYqozXCr8Kgu56Ku5xViH5gMlh5O1K4uLiOKcOt1axM1GdouJaFRX4mdFZR2Fa++IV55
FqUE0SSxOGjkUOjDoVYVBySF/wBdTxxV311PHFXfXU8cVd9dTxxVSub5RCxB3ptirO57tba0kkrR
YYy3jQKtf4YFYDZXoMCknegwqiPrqeOKu+up44q766njirvrqeOKu+up44q766njirvrqeOKu+up
44q766njirvrqeOKqum6l6es2ZB2dyhH+uCv8cCsi8zTD9EvIT/dujfewX/jbFWLi9SnXCrf11PH
FXfXU8cVd9dTxxV311fHFV/6Uk/363/BHFXonlO7tZvKw+qTi5uUk5XMaHk6NzpRl6j4RgKQyi1n
SWJSprgVWxVD3EtJo4uXH1DSo69CcVSDzNeecLdGtvLWkCZ2HxahNLCFBI/ZjZwzEeLbexwhXlet
fl7+aGryTXN9avdXUisA8lxbmla0VR6lFFT0G2SsISzyn+V35qWejx2d/pqxvbkpETc27Vj6r9l2
6dMiqc/8q3/ML/lhT/kfF/zVjau/5Vv+YX/LCn/I+L/mrG1d/wAq3/ML/lhT/kfF/wA1Y2rv+Vb/
AJhf8sKf8j4v+asbVSufy98+xR+pLZosakF29eI0A67BsbVk+omWfT7qGIVllikSMVpVmUgbn3xV
i1n+XX5gNAhWxShAp+/i/wCasbVX/wCVb/mF/wAsKf8AI+L/AJqxtXf8q3/ML/lhT/kfF/zVjau/
5Vv+YX/LCn/I+L/mrG1d/wAq3/ML/lhT/kfF/wA1Y2rv+Vb/AJhf8sKf8j4v+asbV3/Kt/zC/wCW
FP8AkfF/zVjau/5Vv+YX/LCn/I+L/mrG1d/yrf8AML/lhT/kfF/zVjau/wCVb/mF/wAsKf8AI+L/
AJqxtXf8q3/ML/lhT/kfF/zVjarV8i+eLG9tLu6tES3gmjkmcTRNRFYFtg1emNqnuu2t7qWlTWVk
oe6l4ekpIUEq6t1NB0GKpGn5cfmCVBFin/I+L/mrG1b/AOVb/mF/ywp/yPi/5qxtXf8AKt/zC/5Y
U/5Hxf8ANWNq7/lW/wCYX/LCn/I+L/mrG1d/yrf8wv8AlhT/AJHxf81Y2rv+Vb/mF/ywp/yPi/5q
xtVll+X35mafei8sbVre5UkiWO4gHU1of3m4PgclYV6b5Zn86TD0df0xbSdRUahDJCyOR/vyJXYg
nxX7hkSlklvOWZo2I5oaNTAqHvjxu7Vq0+MD79sVR+KuxV2KuxV2KuxV2KoLWRXT5h/kN+rFXmUM
9ZkFerAfjhQ9Q03/AHkj/wBUfqwJRWKuxV2KuxV2KuxV2KuxV2KuxVJ/M7BdMmJ7I36sVYPos3PV
Lda9W/UDhQ9Oi/u1+WBK7FXYq7FXYq7FXYq7FUt08lr+7JNf3hH3bYqv1ID1Lc+Esf8AxIYqj8Vd
irsVdirsVdirsVSnzVqtlpHl6/1O9JFrZwvLLShJCj7Kg03Y7D3xV49LrAt7J7+ICT0ozPGpNA3F
eYFR45JD1/ylq1vq/l6x1K3BWK5iDBT1Uj4WU/6rAjIpTjFXYq7FXYq7FXYq7FXYq7FXYqwL80/N
Y0iPT9OijEt1qkvpnkaKkIKh3+fxfCP9rCFYHf8AmlvLtq2sLALk2hVvQJ48gzBGoe1FYnCh7dpF
/b6jpVnqFsSba8gjuICRQlJUDrUfI5FKLxV2KuxV2KuxV2KuxVK9JWlxdnxmkP8Aw5xVW1OgWNia
UdTX5HFUdirsVdirsVdirsVdiqV+aNAtvMPl7UNFuWKRX8LQmQCpRiPhcCorxajUxV4Bd/lJ+baP
Hotnbxvp6cYhqL3MYg4ig5emW9biPDgflhtXvXk3y4vlvyzp+iiY3DWkdJJ225yOxeRgOwLsaDww
KnOKuxV2KuxV2KuxV2KuxV2KuxVgf5qeQbnzNbWt1YMBf2XIBDQc0eh2JI3Uj9eNq8uH5V/mDrMg
sLiN4rcnhJdXDERorfCzBW4s5Ck0oPpGG1fQek6bb6XpVlpltU29jBFbQlt24QoEWvvRcCorFXYq
7FXYq7FXYq7FUt0kHlOx7yOfvY4qqavtbFuvHenyxVHYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUt0WphZq1qSa+NTiqrqwraP8sVRo6DFXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqlugj/Qk
+WKojUh/oz/LFVeEgwxkbgqKH6MVX4q7FXYq7FXYq7FXYq7FXhf5n/mT5ms/Oc2nadcva2OncEMU
Z4mVmQO7Mw+L9qg3ptXN1pNHCWKzzLzvaOsyeIYxJiB3Mh8o/mtPNbob8fW4Ds0q0WZD4MNlb8Pn
mvz6cwlRYabtecNsnqHf1ekabqun6lB61lOsqftAbMvsyncfTmMQ7/DnhkFxNovA3OxV2KuxV2Ku
xV2KuxV2KuxV2KuxV2KuxV2KuxV2KtOQEYnoATiqB0ZaWafLFVe+Fbdvliq6yINnAQagxoQf9iMV
VsVdirsVdirsVdirsVdir5m/NOOvn/WD/wAWJ/yaTOj0R/dReS7QP7+X46JFpl7cWFwJoTUHaSM9
GHgcszYo5I0XCt6Joeql1S90+ZopBseJoynurZzufHLHKi2YpGJuJos/0Xz4Txh1VN+n1mMfiyD/
AI1+7KOJ3mm7U6ZPmy+3uYLmJZoJFlibo6moyTuITEhYNhUxZOxV2KuxV2KuxV2KuxV2KuxV2Kux
V2KuxV2KqdywS3lY9FRiaewxVDaSCLRPliqteisDfLFVumf8c21/4wx/8RGKonFXYq7FXYq7FXYq
7FXYq+cPzNjr581Y/wDFif8AJpM6DRn90HkO0T+/l+OjGhFmRbg2jtLvbjT7kTQnY7SRnow8DlGf
DHJGimM6egabdW1/bLPAag7Mp6qfA5zOfFLHKpOXCpCwm2n3l7YS+paytGf2gN1b5jocpGSnIxZJ
YzcTTMNL83W89I71fQl/34N4z/FctjmB5u4wa8S2lsfsT9WVlDKQyncEbgjLnYg23irsVdirsVdi
rsVdirsVdirsVdirsVdiqhqDqlhcu32Vicn5BTiqnpv+8yfLFVa6/uW+WKrNN/459t/xiT/iIxVE
Yq7FXYq7FXYq7FXYq7FXz1+ZEdfO+qn/AIsT/k0mb3Sn92Hje0j+/l+OjHBFl9uDa8Q4LRaP0u9u
dPuRNCag7SRnow8DmPqMEcsaLOGQxNh6Dpt3bX9ss8BqDs6nqp8DnLajFLFLhk7XFITFhGrDmOZN
vCmGn6hfWJHovWPvE26n6O30ZKGcx5ORhyyhyZNYa7a3VFk/cy/ysdj8jmZj1MZeRdni1UZc9imW
ZDkuxV2KuxV2KuxV2KuxV2KuxV2KuxVC6tvpd4P+KJP+IHFXacKWyfLFVW6/uW+WKqWl1/R1vX/f
a/qxVFYq7FXYq7FXYq7FXYq7FXgn5hx1856mf8tP+Ta5utMf3YeL7TP+ES/HRj4hy63AteIcFrao
sOC0WjtLvbnT7kTQnY7SRnow8DmPqcEcseGTZizGBsPQ9Nu7a/tlngNR0ZT1U+BzktThlilwyd9h
yRyRsI0Q5jGTfwrxDkTJPCmFlqN1bUWvqRD9hu3yPbL8WslDzDk48so+5N7bV7CeYQCVVuaV9BiA
1PbxzZ4dTDJy5uzhCUocYB4e9GZkMXYq7FXYq7FXYq7FXYq7FXYqhdV/45l3/wAYX/4icVXWP+86
/LFVSf8Au2+WKqWmEGwgp/KB92KonFXYq7FXYq7FXYq7FXYq8O8+x1836kf8tP8Ak2ubfTn0B4nt
Q/4RL8dEiEOW24FqghwWi14hwWi14hwWi0fpd7c6fciaE7HaSM9GHgcxtTp45o8Mv7G7BqJYpWHo
Wm3dtf2yzwHboyHqp8DnH6nBLDLhk9Pp80cseKKNEOYpk5HCl2s6rHYp6UdGumGw7KPE4Ru7nsvs
o5zxS2xj7fIMV/ePIZWYmQnkXrvXxwmT2gEYx4QKAZPo/m6/tgsV3W6hG3I/3g+nv9OZeHtGUdpb
j7XU6rsyE94ek/YzCw1Oyvo+dtIGP7SHZh8xm3w6iGQXEuhzaeeM1IIrLml2KuxV2KuxV2KuxV2K
oTVyRpd1T/fTD7xTFVSy/uF+WKqk/wDdn5Yqh9I/450PyP8AxI4qi8VdirsVdirsVdirsVdirxjz
vHXzXqB/y1/5Nrm0wH0B4ftQ/wCET/HRJRDlluvtUEOC0WvEOC0WqCHBa2vEOC0Wj9LvbnT7kTQn
Y7SRnow8DmLqtPHNHhl/Y36bVSwy4osqufM9qbQG0Ia5cbxnrGf8ofqzkM+mlimYyfRexdH+ciMn
LH9/l+1jpDyO0khLOxqzHqTlJk9vECIobAKqRZAyYGSukWVmTWZImD1InDxsUdejKaEfdkRkMTYN
FqnRFFlmg6zcXLm3uaM4WqSAUrTsaZvOztfLJLglz73SazSxgOKPJO83DrnYq7FXYq7FXYq7FUJq
wrptyP8Ais4qq2gpCvyxVfP/AHZ+WKofSP8AjnQ/I/8AEjiqLxV2KuxV2KuxV2KuxV2KvI/OUdfM
98f8tf8AiC5sMJ9IeF7VP+Ez/HRKFhyy3XWvWHBaLVBDgtbVFhwWi16w4LRaT+Yteh0yP0YqPeuP
hXqEB/ab+AzHzZuEbc3r/Zf2Xnr5+JkuOnif9N5D9J+A35RDTdbvrK+N4rmR3P79WOzj3/hmj1EO
Pm+2x0mMYxjiOGMRQro9O0fULTU7Rbm2ao6Oh+0jfysM0uSJiaLqM8JYzRTNIspMnGMldIsrMmsy
Rtnp1zctxhQtTq3QD5nJ4cE8pqItx8ueMBuWTaVo8dlWRm5zEUqOgHtnR6Hs8YfUTcnUajVHJsOS
Y5snEdirsVdirsVdirsVQessV0y4I/lp95AxVXtf7lfliq6f+7PyxVD6R/xzofkf+JHFUXirsVdi
rsVdirsVdirsVeW+bIq+Yr0/5S/8QXM3EfSHg+1j/hM/x0CWLDk7dba9YcFraosOC0WvWHI2i0j8
z+Y4dJi9CGkl+4+FeoQH9pv4DISlT13sx7Mz18/EyenBH/ZeQ/Sflvy51LNLNK0srF5HPJ3bcknM
LI+3YMUMcBCA4Yx2ADhmHNyoploms3ek3i3Fuag7SxH7Lr4H+BzAzYxIUVzYI5Y0XsHl69g1y2Se
wBkJ2eL9pG7hvDNV4EzLhAsvK6uBwGp7MusPLaLR7s8j/vpen0n+mbfTdjjnkPwdJm15O0U6jjjj
QJGoRR0UCgzdwgIigKDr5SJNldkkOxV2KuxV2KuxV2KuxVBa3/xy7j5D/iQxVEWv9yvyxVdP/dn5
Yqh9I/450PyP/EjiqLxV2KuxV2KuxV2KuxV2KvOPM0Vdeuz/AJS/8QGZUDs8D2uf8Jn+OgS4Q4bd
ZaosOC1tesOC0Wx/zZ5og0eL6vARJqMg+FOojB/ab+AyURb1nsz7NS10/EyenBH/AGXkP0n9PLmU
s808zzTOZJZDyd2NSSchMPteDFHHAQgOGMdgA0MxJuVFXtre4uZ0gt42mnkPGOKNSzMT2AG5zEmG
0zERZNAPTvKX5KX91wuvMEhs4DQizjIMzD/LbdU/E/LGOlv6nn9b7Rxh6cI4j3nl+165o+h6To1o
LTTLVLaEUqEHxMRtV2PxMfcnMuGOMRQDyeo1WTNLiyEyKOybjuxV2KuxV2KuxV2KuxV2KuxVBa3/
AMcu4+Q/4kMVRFr/AHK/LFV0/wDdn5Yqh9I/450PyP8AxI4qi8VdirsVdirsVdirsVdirA/MEVdZ
uj/lD/iIy6J2fPu2D/hM/f8AoCBEOG3W2vEYGC0Wxvzh5ut9FhNvb0k1KQfAnURg/tv/AAGXYsRl
ueT1fs17Ny10/EybYB/svIfpP6eXKZp5riZ553MkshLO7GpJPfMiQp9owYo44iEBwxGwAaUEmg3J
6DMSbkxeheUfye8waxwudSrpdgd/3i/v3H+TGacfm33HMWQt1mr7bxYtoeuX2fN7L5a8m+X/AC5B
6emWwWUikl0/xTP/AKz/AMBQYiIDy2r1+XOfWdu7oneFw3Yq7FXYq7FXYq7FXYq7FXYq7FXYqgtb
/wCOXcfIf8SGKoi1/uV+WKrp/wC7PyxVD6SCNOhrtsf1nFUXirsVdirsVdirsVdirsVYVrv/AB1r
j5j/AIiMsjyfPe2P8an7/wBAQGF1jGPOPnKHRYTbWxEmpyD4E6iMH9t/4DMvT6Yz3P0vVezns5LW
y48m2Af7LyH6T+nlyaa4muJ3nncyTSEs7sakk9zmfKIAoPseDFHHERiKiOQZb5R/LLzN5j4TRxfU
9Obc3s4IUj/itftP9G3vmDmygNOp7Sx4djvLuD2zyl+Wnlry2Elhi+t6gvW+nAZwf+K1+yn0b++Y
MpkvO6vtLLm2JqPcGV5B17sVdirsVdirsVdirsVdirsVdirsVdirsVQWt/8AHLuPkP8AiQxVEWv9
yvyxVufaJvliqlpu9hB/qDFUTirsVdirsVdirsVdirsVYVrv/HWuPmP+IjLI8nz3tj/Gp+/9AYR5
z85waJCba2Ik1OQfAnURg/tv/AZn6TRnIbP0u19nvZ6Wtlxz2wj/AGXkP0n9PKBeXvJ/mrzbevJZ
wPMHetxfzErEpPUs56n2Wp9s2WfNDEKPyfVzlw6aAiKiANgHtPk/8m/Lui8LnUQNU1BaHlKv7hD/
AJEff5tX6M0ubVyny2DqNT2nkntH0x+16AAAKDYDoMxHWOxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ku
xV2KuxVBa0CdLuAP5QfuIOKoi1/uV+WKuuv7lvliqzTRTT7b/jGn6sVRGKuxV2KuxV2KuxV2KuxV
ifnDQtdnSWfQlie8motJ24KhpTn0NaDtmRpzDi9f0uly9iY82q8SZ/dn6h1NfrYz5X/JCziuDqPm
m5/Sl87c2t0LCDkd6uxo8n4D2OZ2o7TJHDjHDF6qWu4YiGIcERsHp1vb29tCkFvEkMEY4xxRqERR
4BRQDNUSSbLgEkmyqYEOxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KoXVf+Obc/8Yziqra/
3K/LFWrs0gb5Yqt03fTrU+MMf/ERiqIxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ku
xV2KuxV2KuxV2KuxV2KuxV2KoXVhXTLr/jE5+4VxVfZf3C/LFWr80t3+WKqlspW2iU9Qig/QMVVM
VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqF1X/jmX
ftDIf+FOKrrA1t0+WKtagpa3YDqRiqiutacqgczsKfZb+mNK79N6d/vw/wDAn+mGld+m9O/34f8A
gT/TGld+m9O/34f+BP8ATGld+m9O/wB+H/gT/TGld+m9O/34f+BP9MaV36b07/fh/wCBP9MaV36b
07/fh/4E/wBMaV36b07/AH4f+BP9MaV36b07/fh/4E/0xpXfpvTv9+H/AIE/0xpXfpvTv9+H/gT/
AExpXHXNPH7ZP+xONKt/T1h4v/wONK79PWHi/wDwONK79PWHi/8AwONK79PWHi//AAONK79PWHi/
/A40rv09YeL/APA40rv09YeL/wDA40rv09YeL/8AA40rv09YeL/8DjSu/T1h4v8A8DjSu/T1h4v/
AMDjSu/T1h4v/wADjSu/T1h4v/wONK79PWHi/wDwONK79PWHi/8AwONK79PWHi//AAONKp3WsWE1
tLD8R9RGShX+YUxpUZYIVt1B60wKiGUEUOKoc6bZk1MSf8CMVa/Rll/vlP8AgRirv0ZZf75T/gRi
rv0ZZf75T/gRirv0ZZf75T/gRirv0ZZf75T/AIEYq79GWX++U/4EYq79GWX++U/4EYq79GWX++U/
4EYq79GWX++U/wCBGKu/Rll/vlP+BGKu/Rll/vlP+BGKu/Rll/vlP+BGKu/Rll/vlP8AgRirv0ZZ
f75T/gRirv0ZZf75T/gRirv0ZZf75T/gRirv0ZZf75T/AIEYq79GWX++U/4EYq79GWX++U/4EYq7
9GWX++U/4EYq79GWX++U/wCBGKu/Rll/vlP+BGKu/Rll/vlP+BGKu/Rll/vlP+BGKu/Rll/vlP8A
gRirv0ZZf75T/gRirv0ZZf75T/gRirv0ZZf75T/gRira6dZqaiJK/IYqiQABQYq//9k=</xmpGImg:image> - </rdf:li> - </rdf:Alt> - </xmp:Thumbnails> - </rdf:Description> - <rdf:Description rdf:about="" - xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" - xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" - xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"> - <xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass> - <xmpMM:OriginalDocumentID>uuid:65E6390686CF11DBA6E2D887CEACB407</xmpMM:OriginalDocumentID> - <xmpMM:DocumentID>xmp.did:F87F1174072068118A6DF333610584B2</xmpMM:DocumentID> - <xmpMM:InstanceID>uuid:15a3ab41-6703-e944-a5ec-63770d002408</xmpMM:InstanceID> - <xmpMM:DerivedFrom rdf:parseType="Resource"> - <stRef:instanceID>uuid:52d3fbd8-d8d7-2543-8e0c-81d9ea137ecb</stRef:instanceID> - <stRef:documentID>xmp.did:8CF5709C0E20681188C6A12CE4B46A4D</stRef:documentID> - <stRef:originalDocumentID>uuid:65E6390686CF11DBA6E2D887CEACB407</stRef:originalDocumentID> - <stRef:renditionClass>proof:pdf</stRef:renditionClass> - </xmpMM:DerivedFrom> - <xmpMM:History> - <rdf:Seq> - <rdf:li rdf:parseType="Resource"> - <stEvt:action>saved</stEvt:action> - <stEvt:instanceID>xmp.iid:F87F1174072068118A6DF333610584B2</stEvt:instanceID> - <stEvt:when>2012-06-25T18:52:34-07:00</stEvt:when> - <stEvt:softwareAgent>Adobe Illustrator CS5</stEvt:softwareAgent> - <stEvt:changed>/</stEvt:changed> - </rdf:li> - </rdf:Seq> - </xmpMM:History> - </rdf:Description> - <rdf:Description rdf:about="" - xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/"> - <illustrator:StartupProfile>Web</illustrator:StartupProfile> - <illustrator:Type>Document</illustrator:Type> - </rdf:Description> - <rdf:Description rdf:about="" - xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/" - xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#" - xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/"> - <xmpTPg:NPages>1</xmpTPg:NPages> - <xmpTPg:HasVisibleTransparency>True</xmpTPg:HasVisibleTransparency> - <xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint> - <xmpTPg:MaxPageSize rdf:parseType="Resource"> - <stDim:w>11.111111</stDim:w> - <stDim:h>8.333333</stDim:h> - <stDim:unit>Inches</stDim:unit> - </xmpTPg:MaxPageSize> - <xmpTPg:PlateNames> - <rdf:Seq> - <rdf:li>Cyan</rdf:li> - <rdf:li>Magenta</rdf:li> - <rdf:li>Yellow</rdf:li> - <rdf:li>Black</rdf:li> - </rdf:Seq> - </xmpTPg:PlateNames> - <xmpTPg:SwatchGroups> - <rdf:Seq> - <rdf:li rdf:parseType="Resource"> - <xmpG:groupName>Default Swatch Group</xmpG:groupName> - <xmpG:groupType>0</xmpG:groupType> - <xmpG:Colorants> - <rdf:Seq> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>White</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>255</xmpG:red> - <xmpG:green>255</xmpG:green> - <xmpG:blue>255</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>Black</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>0</xmpG:green> - <xmpG:blue>0</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>RGB Red</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>255</xmpG:red> - <xmpG:green>0</xmpG:green> - <xmpG:blue>0</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>RGB Yellow</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>255</xmpG:red> - <xmpG:green>255</xmpG:green> - <xmpG:blue>0</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>RGB Green</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>255</xmpG:green> - <xmpG:blue>0</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>RGB Cyan</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>255</xmpG:green> - <xmpG:blue>255</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>RGB Blue</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>0</xmpG:green> - <xmpG:blue>255</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>RGB Magenta</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>255</xmpG:red> - <xmpG:green>0</xmpG:green> - <xmpG:blue>255</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=193 G=39 B=45</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>193</xmpG:red> - <xmpG:green>39</xmpG:green> - <xmpG:blue>45</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=237 G=28 B=36</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>237</xmpG:red> - <xmpG:green>28</xmpG:green> - <xmpG:blue>36</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=241 G=90 B=36</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>241</xmpG:red> - <xmpG:green>90</xmpG:green> - <xmpG:blue>36</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=247 G=147 B=30</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>247</xmpG:red> - <xmpG:green>147</xmpG:green> - <xmpG:blue>30</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=251 G=176 B=59</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>251</xmpG:red> - <xmpG:green>176</xmpG:green> - <xmpG:blue>59</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=252 G=238 B=33</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>252</xmpG:red> - <xmpG:green>238</xmpG:green> - <xmpG:blue>33</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=217 G=224 B=33</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>217</xmpG:red> - <xmpG:green>224</xmpG:green> - <xmpG:blue>33</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=140 G=198 B=63</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>140</xmpG:red> - <xmpG:green>198</xmpG:green> - <xmpG:blue>63</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=57 G=181 B=74</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>57</xmpG:red> - <xmpG:green>181</xmpG:green> - <xmpG:blue>74</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=0 G=146 B=69</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>146</xmpG:green> - <xmpG:blue>69</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=0 G=104 B=55</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>104</xmpG:green> - <xmpG:blue>55</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=34 G=181 B=115</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>34</xmpG:red> - <xmpG:green>181</xmpG:green> - <xmpG:blue>115</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=0 G=169 B=157</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>169</xmpG:green> - <xmpG:blue>157</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=41 G=171 B=226</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>41</xmpG:red> - <xmpG:green>171</xmpG:green> - <xmpG:blue>226</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=0 G=113 B=188</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>113</xmpG:green> - <xmpG:blue>188</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=46 G=49 B=146</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>46</xmpG:red> - <xmpG:green>49</xmpG:green> - <xmpG:blue>146</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=27 G=20 B=100</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>27</xmpG:red> - <xmpG:green>20</xmpG:green> - <xmpG:blue>100</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=102 G=45 B=145</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>102</xmpG:red> - <xmpG:green>45</xmpG:green> - <xmpG:blue>145</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=147 G=39 B=143</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>147</xmpG:red> - <xmpG:green>39</xmpG:green> - <xmpG:blue>143</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=158 G=0 B=93</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>158</xmpG:red> - <xmpG:green>0</xmpG:green> - <xmpG:blue>93</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=212 G=20 B=90</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>212</xmpG:red> - <xmpG:green>20</xmpG:green> - <xmpG:blue>90</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=237 G=30 B=121</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>237</xmpG:red> - <xmpG:green>30</xmpG:green> - <xmpG:blue>121</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=199 G=178 B=153</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>199</xmpG:red> - <xmpG:green>178</xmpG:green> - <xmpG:blue>153</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=153 G=134 B=117</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>153</xmpG:red> - <xmpG:green>134</xmpG:green> - <xmpG:blue>117</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=115 G=99 B=87</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>115</xmpG:red> - <xmpG:green>99</xmpG:green> - <xmpG:blue>87</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=83 G=71 B=65</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>83</xmpG:red> - <xmpG:green>71</xmpG:green> - <xmpG:blue>65</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=198 G=156 B=109</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>198</xmpG:red> - <xmpG:green>156</xmpG:green> - <xmpG:blue>109</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=166 G=124 B=82</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>166</xmpG:red> - <xmpG:green>124</xmpG:green> - <xmpG:blue>82</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=140 G=98 B=57</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>140</xmpG:red> - <xmpG:green>98</xmpG:green> - <xmpG:blue>57</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=117 G=76 B=36</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>117</xmpG:red> - <xmpG:green>76</xmpG:green> - <xmpG:blue>36</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=96 G=56 B=19</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>96</xmpG:red> - <xmpG:green>56</xmpG:green> - <xmpG:blue>19</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=66 G=33 B=11</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>66</xmpG:red> - <xmpG:green>33</xmpG:green> - <xmpG:blue>11</xmpG:blue> - </rdf:li> - </rdf:Seq> - </xmpG:Colorants> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:groupName>Grays</xmpG:groupName> - <xmpG:groupType>1</xmpG:groupType> - <xmpG:Colorants> - <rdf:Seq> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=0 G=0 B=0</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>0</xmpG:red> - <xmpG:green>0</xmpG:green> - <xmpG:blue>0</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=26 G=26 B=26</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>26</xmpG:red> - <xmpG:green>26</xmpG:green> - <xmpG:blue>26</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=51 G=51 B=51</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>51</xmpG:red> - <xmpG:green>51</xmpG:green> - <xmpG:blue>51</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=77 G=77 B=77</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>77</xmpG:red> - <xmpG:green>77</xmpG:green> - <xmpG:blue>77</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=102 G=102 B=102</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>102</xmpG:red> - <xmpG:green>102</xmpG:green> - <xmpG:blue>102</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=128 G=128 B=128</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>128</xmpG:red> - <xmpG:green>128</xmpG:green> - <xmpG:blue>128</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=153 G=153 B=153</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>153</xmpG:red> - <xmpG:green>153</xmpG:green> - <xmpG:blue>153</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=179 G=179 B=179</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>179</xmpG:red> - <xmpG:green>179</xmpG:green> - <xmpG:blue>179</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=204 G=204 B=204</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>204</xmpG:red> - <xmpG:green>204</xmpG:green> - <xmpG:blue>204</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=230 G=230 B=230</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>230</xmpG:red> - <xmpG:green>230</xmpG:green> - <xmpG:blue>230</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=242 G=242 B=242</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>242</xmpG:red> - <xmpG:green>242</xmpG:green> - <xmpG:blue>242</xmpG:blue> - </rdf:li> - </rdf:Seq> - </xmpG:Colorants> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:groupName>Web Color Group</xmpG:groupName> - <xmpG:groupType>1</xmpG:groupType> - <xmpG:Colorants> - <rdf:Seq> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=63 G=169 B=245</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>63</xmpG:red> - <xmpG:green>169</xmpG:green> - <xmpG:blue>245</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=122 G=201 B=67</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>122</xmpG:red> - <xmpG:green>201</xmpG:green> - <xmpG:blue>67</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=255 G=147 B=30</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>255</xmpG:red> - <xmpG:green>147</xmpG:green> - <xmpG:blue>30</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=255 G=29 B=37</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>255</xmpG:red> - <xmpG:green>29</xmpG:green> - <xmpG:blue>37</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=255 G=123 B=172</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>255</xmpG:red> - <xmpG:green>123</xmpG:green> - <xmpG:blue>172</xmpG:blue> - </rdf:li> - <rdf:li rdf:parseType="Resource"> - <xmpG:swatchName>R=189 G=204 B=212</xmpG:swatchName> - <xmpG:mode>RGB</xmpG:mode> - <xmpG:type>PROCESS</xmpG:type> - <xmpG:red>189</xmpG:red> - <xmpG:green>204</xmpG:green> - <xmpG:blue>212</xmpG:blue> - </rdf:li> - </rdf:Seq> - </xmpG:Colorants> - </rdf:li> - </rdf:Seq> - </xmpTPg:SwatchGroups> - </rdf:Description> - <rdf:Description rdf:about="" - xmlns:pdf="http://ns.adobe.com/pdf/1.3/"> - <pdf:Producer>Adobe PDF library 9.90</pdf:Producer> - </rdf:Description> - </rdf:RDF> -</x:xmpmeta> - - - - - - - - - - - - - - - - - - - - - -<?xpacket end="w"?>
endstream
endobj
3 0 obj
<</Count 1/Kids[7 0 R]/Type/Pages>>
endobj
7 0 obj
<</ArtBox[94.0 7.0 707.0 595.0]/BleedBox[0.0 0.0 800.0 600.0]/Contents 8 0 R/Group 9 0 R/LastModified(D:20120625185236-07'00')/MediaBox[0.0 0.0 800.0 600.0]/Parent 3 0 R/PieceInfo<</Illustrator 10 0 R>>/Resources<</ColorSpace<</CS0 11 0 R/CS1 11 0 R>>/ExtGState<</GS0 12 0 R/GS1 13 0 R>>/Properties<</MC0 5 0 R>>/Shading<</Sh0 14 0 R/Sh1 15 0 R/Sh2 16 0 R/Sh3 17 0 R/Sh4 18 0 R/Sh5 19 0 R>>/XObject<</Fm0 20 0 R/Fm1 21 0 R/Fm2 22 0 R>>>>/Thumb 23 0 R/TrimBox[0.0 0.0 800.0 600.0]/Type/Page>>
endobj
8 0 obj
<</Filter/FlateDecode/Length 2230>>stream
-H‰Œ—ËŽ$·E÷ùübóýت%{%‚Ö¾`kÝ䘿÷¹Á¬ª¬ì1`
TI2^7n_þñê^~~
î‡_Ýö×\Á
þ¿èá?ÿÚþéþ@þò÷_ƒûýëöò·÷à~üsûe{yErýê¢ýs_¯hQÔ¢¿þE—Cð¹ÍæjŒ>õ<Ýõ}Ó§÷d?suuøÐ‡‹!ûžx
>–è¾m±7?ÚpZ†K!ú̺ßSäuøÙº»¤á+ë¯Û%JCÒYQÝÛv™|*±šAß¶<ÞŸÒÄŽÜ’uºKAagŸ!³1q@ŒHRj,6eÚ;ƶÈÑãºK)øž±fbrBKBgÕÅæËĹ^8ººR}Â’-fŒ¬ÝuÅ »9|guÇÅ&ßk—:Ðãd3©ŠC艽M8+Vb¶lëX[}åÄK~X{–}ɶQ‰u UÈ_$š‘ÃñiŽd¢^}žQçuŸIÀ%ކ¶ê;™øØ2OuÌ%J¶/c´íª™à»s´?¶ƒœàk›N¿Q¿£,ô<PSIv)=»Ò'V¹;j.żLšsô8\$EŠMá¼Lè -‡°§½1|‰Ã#«i&7ˆBX®6Ü(Óñ'6 RHžC„¼ªÈñ:xRaGeðm˜‚ˆï3DíÆl“”Mº -ÙB6›oÉ$àŒæ&‡
3‹ÈÒ7„AKSΆ¥Þ쬷ú (`0H‚ -xübA=”á#1S&h)C@ß㈇Aµ¸KSù ·ÏüÈÓˆ(¨¹,ØZ‡ -n9-”2¿Puú ê)¶xsò -£?!^êÛâŠjÔ~‰œ›E—´Š¡ò§
ν?gK¡l½fy×âOº -‡~ö¼gG$üûqfŽkfþn“®Št¥=Ñ*e÷x`ÔTµ‡}VÁæú…&zˆ[^hnÙÆŸ½©«ÙEãa\4vk›²ï[—–uǪd.HóÖÀâx3¯&Æ5,Än&õu>k*ņœlD`ÒeòÒrŠ•k:%³2¦Ö¨±ú`aL!$…”U©²‰9pn™Ì?D*5ØÄäq*Mg AkºZà-˜®t_ Á6kX¼™ Ïb”¸A$fá4›½§ªÁç,aSAß%…9=¼
Í -5'ÉÛCRëºÔ®04Vö3R*xg‰ö°–Ú)3ýÙ«ï׫¢Î³€æ\ŠÁb¶é¤Î„!«.QLy–¼mE¬LÑ+‘Âaò -ü w”!“‰Ž‰øøüXÁýÍ@˜áW’¸~y}M¯ñizMªlZ].‰¥SMA7\ìOêøC9g¤ç á–©Û!î$̲¹»ÝI¢ÎUsÍÕ4‡l-SçvLÉAäþzÝNJmjLwIšƒH{×Dr¶ôS”4òGÆ""pæLô¤ãó¾$§•kî~êE‰F`‡ò#Lé)Lç ¼[Pºú¦¨Ö†3KI2Û´U`°t‚ž“Õ3л½3”TÝ}‡£Ó³d>ü#øç›L¤ùýYÏÓJ3öó˾NÇû¬17³ÉåªY§µp¼âäg¬ÜeŠ¿AHïKB’ÈQ1ŒßxÈJ@Á]²¹AUK]aLƒšHÂu{~[Ñ•Š®]q´‚‡[®% vb7ĉî&ezvvg?¢,ƒˆöaó6üK¡Ì§çÃA¡74ê‚3áì§"*ŸŠèáÏ»5…Ao£átæªM|ÝŠ‡ØtÃñI°r®§¶>G茆\”ÖÐ!–K&X´}›iχ%¤0',
©ø'5âðð°Þ=üéçW‡›ÿ` -8;ZDo4a*;['SVmH.]]4a"c]In"BYY'W"&B_"uWn*W!HPAUd-Pr8ni5hFd_XX^-P.R -&jM#',fKGKkB"k;2tq_V8B2M*h\6%E")-<!M)_-rr*1"Ug]//^]90Oi[Bd/VUbRB- -kPc+S\miA\f!2(u"LGOK\7-EaB+8@7X!a6jUE[3%(q-t')8TL6e+2%q!@c6+EkL7J -1aue$:Y^CSLH,@FO;tW5\''fd_gi#)T\/_-9eLp6Q7Fb[ceoiOf.idg!T3.g-VVmK -lh$@qW9;?/bBH*/\sA,b7m6.oK_LFEgMlW7RW\#WYk@#r%QHLU'7(s`LI2KKI224N -@`5rI0fh7e%tn-+T?s\=#aj/H*E)()T)@[QK/Bs>(7sckFH#-[JAh%Bj[KD*9HkVh -m20mdQ>)a?*j"-Y<)5J)dorid(bpY(7W=`[?Xf,8e$'D=$fP/KrIgG+;tZ:s#Y -;4rG`a_o%mh(n<6Z;%&aiW@]$;,R+o$;dCQ,12l'$)kp6>e/XVm2bKd@9YqKQ=]Ll -nln\7@.5\;#ioFIp9K5*b-Ig(hX=;(W!R5=4Ad*?LBdiTW[O/J"[\O:8I)PmN-bIg -<abCFRXqOf6dC,j8iPWd\-Xi/%u3NqNMu,9&GUgIVk;H:VP_]S+Y&@\'pOkHJt%9o -3n3&fC4;D^$M*gZ\d&I2Zn[[-K7^G:k4lC4QiIVj#k0XkdMrQ.LXl`NVYCFL!nNh` -`!R&TH'YAh^%MTgONPERL].G3LSWRW=dWjT,RK_**!B\PE=D,/s*Uo`YmnGZLN;i[ -iLlp,3$q9@N[W\;gBeOHG`KXS8d<]nMC))Cc[*A+f4rU_V9TWHr\*q[!2KK+LB~>
endstream
endobj
25 0 obj
[/Indexed/DeviceRGB 255 26 0 R]
endobj
26 0 obj
<</Filter[/ASCII85Decode/FlateDecode]/Length 428>>stream
-8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 -b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` -E1r!/,*0[*9.aFIR2&b-C#s<Xl5FH@[<=!#6V)uDBXnIr.F>oRZ7Dl%MLY\.?d>Mn -6%Q2oYfNRF$$+ON<+]RUJmC0I<jlL.oXisZ;SYU[/7#<&37rclQKqeJe#,UF7Rgb1 -VNWFKf>nDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j<etJICj7e7nPMb=O6S7UOH< -PO7r\I.Hu&e0d&E<.')fERr/l+*W,)q^D*ai5<uuLX.7g/>$XKrcYp0n+Xl_nU*O( -l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>
endstream
endobj
20 0 obj
<</BBox[94.0 595.0 707.0 7.0]/Group 27 0 R/Length 40/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 28 0 R>>/ExtGState<</GS0 29 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 30 0 R>>>>/Subtype/Form>>stream
-q -/GS0 gs -613 0 0 588 94 7 cm -/Im0 Do -Q -
endstream
endobj
21 0 obj
<</BBox[495.0 491.0 545.0 389.0]/Group 31 0 R/Length 42/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 28 0 R>>/ExtGState<</GS0 32 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 33 0 R>>>>/Subtype/Form>>stream
-q -/GS0 gs -50 0 0 102 495 389 cm -/Im0 Do -Q -
endstream
endobj
22 0 obj
<</BBox[251.0 494.0 301.0 389.0]/Group 34 0 R/Length 42/Matrix[1.0 0.0 0.0 1.0 0.0 0.0]/Resources<</ColorSpace<</CS0 28 0 R>>/ExtGState<</GS0 35 0 R>>/ProcSet[/PDF/ImageC/ImageI]/XObject<</Im0 36 0 R>>>>/Subtype/Form>>stream
-q -/GS0 gs -50 0 0 105 251 389 cm -/Im0 Do -Q -
endstream
endobj
34 0 obj
<</I false/K false/S/Transparency/Type/Group>>
endobj
36 0 obj
<</BitsPerComponent 8/ColorSpace 28 0 R/Decode[0.0 255.0]/Filter/FlateDecode/Height 105/Intent/RelativeColorimetric/Length 139/Name/X/SMask 37 0 R/Subtype/Image/Type/XObject/Width 50>>stream
-H‰ì×»€ DÑÝÿÿi+•}
’8SßSA"ŸLÏauµy¢\à˜¨7MZ’…Á”@‡Pt])Ê Cè:€ÿŠ¢. -H‰Ì×çWSÙ -Ébs-ÀàrØæ:…DÀa@„Ñ -¸=Ʀ¿OO˜š†ò
u¼²þi×Àøô÷oC&^Õ‚ ¡Žƒ†z7_Z0E¡j¨Áñoÿž‡øÑ‚݃s -ÐP¡¼r{¾BÕæª¦}Ö=Ä÷Šïÿ—b>y,ðY-ü}ü³w¾ÀºZhí.¼?þQšêsCaj/™MìW“3û•I¡µ'Ï-Œì»cs ý½}jlàI¡~LÁ¢uVapF
Ž™†ç 8k§&çxªæ¬= Ÿµc““£s -ø“ -Îsø0:?¡i†–®Os–º\ÿômÿ—‰‰¹„öm ïÈøÄ—O*±Ç¸@hÝÈÊo>îèùÔÕÚpÅ„€o}ªr¿týá›Þ¡ÑÑáþ·-°H™EhÊ}cöá¿ßù~`däs_çÓúËðÅÒˆ˜)^¯åñÛ¿vçy×ÇÁ¡ªËknrt€TÄ6"TÅ·ußé«
OÞ¼ÿØß×ý²ùFÙ1cË™âµÈBV§*ŠË¯ßÞÙó¡§ãÙÝÚÒ#»6EùªÂ¯0 jÓŽÃ%Õ
ÛÞt´·=j¨>_髾Šj õK§°…N>áë3òO”Õ66·<k}Ôt½òÔþmñ+|,² -u¿‰f–¾9Y|[Wß0ù†Ôí»öäçïÍÍÞ’¸&ÜßÍÖJïKmÑïZŽÐÎÍwéÊØÉé[Ó6ÇÇ„º;Zs$ÝG¥YOar…vRÏ€ÐHyìºõëâbV†x8‹y,*AçU³,$„% -™L$à±D0ñ#Œ -a±8‡Ãa1h…œ
¨Ã˜!`„‚ FC -̇s0Ô -ÿÿÿ#
endstream
endobj
39 0 obj
<</Filter/FlateDecode/Length 2574/N 3>>stream
-H‰œ–yTSwÇoÉž•°Ãc
[€°5la‘QIBHØADED„ª•2ÖmtFOE.®cÖ}êÒõ0êè8´×Ž8GNg¦Óïï÷9÷wïïÝß½÷ó - -V³)gB£0ñiœWו8#©8wÕ©•õ8_Å٥ʨQãüÜ«QÊj@é&»A)/ÇÙgº>'K‚ó -€x¯Íú·¶Ò- -¨ê‡†¡Ðnè÷ÐQètº}MA ï —0Óal»Á¾°ŽSàx ¬‚kà&¸^Á£ð>ø0|>_ƒ'á‡ð,ÂG!"F$H:Rˆ”!z¤éF‘Qd?r9‹\A&‘GÈ”ˆrQ¢áhš‹ÊÑ´íE‡Ñ]èaô4zBgÐ×Á–àE#H ‹*B=¡‹0HØIøˆp†p0MxJ$ùD1„˜D, V›‰½ÄÄÄãÄKÄ»ÄY‰dEò"EÒI2’ÔEÚBÚGúŒt™4MzN¦‘Èþär!YKî ’÷?%_&ß#¿¢°(®”0J:EAi¤ôQÆ(Ç()Ó”WT6U@ æP+¨íÔ!ê~êêmêæD¥eÒÔ´å´!ÚïhŸÓ¦h/èº']B/¢éëèÒÓ¿¢?a0nŒhF!ÃÀXÇØÍ8ÅøšñÜŒkæc&5S˜µ™˜6»lö˜Iaº2c˜K™MÌAæ!æEæ#…寒°d¬VÖë(ëk–Íe‹Øél
»—½‡}Ž}ŸCâ¸qâ9 -N'çÎ)Î].ÂuæJ¸rî -î÷wšGä xR^¯‡÷[ÞoÆœchžgÞ`>bþ‰ù$á»ñ¥ü*~ÿ ÿ:ÿ¥…EŒ…ÒbÅ~‹ËÏ,m,£-•–Ý–,¯Y¾´Â¬â*6X[ݱF=3ë·YŸ±~dó ·‘ÛtÛ´¹iÛzÚfÙ6Û~`{ÁvÖÎÞ.ÑNg·Åî”Ý#{¾}´}…ý€ý§ö¸‘j‡‡ÏþŠ™c1X6„Æfm“Ž;'_9 œr:œ8Ýq¦:‹ËœœO:ϸ8¸¤¹´¸ìu¹éJq»–»nv=ëúÌMà–ï¶ÊmÜí¾ÀR 4 ö -n»3Ü£ÜkÜGݯz=Ä•[=¾ô„=ƒ<Ë=G</zÁ^Á^j¯^—¼ Þ¡ÞZïQïBº0FX'Ü+œòáû¤útøŒû<öuñ-ôÝà{Ö÷µ__•ߘß-G”,ê}çïé/÷ñ¿ÀHh8ðm W 2p[àŸƒ¸AiA«‚Ný#8$X¼?øAˆKHIÈ{!7Ä<q†¸Wüy(!46´-ôãÐaÁa†°ƒa†W†ï ¿¿@°@¹`lÁݧYÄŽˆÉH,²$òýÈÉ(Ç(YÔhÔ7ÑÎÑŠèÑ÷b<b*böÅ<Žõ‹ÕÇ~ûL&Y&9‡Ä%ÆuÇMÄsâsã‡ã¿NpJP%ìM˜IJlN<žDHJIÚtCj'•KwKg’C’—%ŸN¡§d§§|“ꙪO=–§%§mL»½Ðu¡váx:H—¦oL¿“!È¨ÉøC&13#s$ó/Y¢¬–¬³ÙÜìâì=ÙOsbsúrnåºçsOæ1óŠòvç=ËËïÏŸ\ä»hÙ¢óÖê‚#…¤Â¼Â…³‹ãoZ<]TÔUt}‰`IÃ’sK—V-ý¤˜Y,+>TB(É/ÙSòƒ,]6*›-•–¾W:#—È7Ë*¢ŠÊe¿ò^YDYÙ}U„j£êAyTù`ù#µD=¬þ¶"©b{ųÊôÊ+¬Ê¯: !kJ4Gµm¥ötµ}uCõ%—®K7YV³©fFŸ¢ßYÕ.©=bàá?SŒîƕƩºÈº‘ºçõyõ‡Ø
Ú†žkï5%4ý¦m–7Ÿlqlio™Z³lG+ÔZÚz²Í¹³mzyâò]íÔöÊö?uøuôw|¿"űN»ÎåwW&®ÜÛe֥ﺱ*|ÕöÕèjõê‰5k¶¬yÝèþ¢Ç¯g°ç‡^yïkEk‡Öþ¸®lÝD_pß¶õÄõÚõ×7DmØÕÏîoê¿»1mãál {àûMÅ›Î
nßLÝlÜ<9”úO -¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿
æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿ -q -/GS0 gs -50 0 0 105 251 389 cm -/Im0 Do -Q -
endstream
endobj
43 0 obj
<</CS 11 0 R/I false/K false/S/Transparency/Type/Group>>
endobj
45 0 obj
<</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 50>>/Filter/FlateDecode/Height 105/Intent/RelativeColorimetric/Length 2256/Name/X/Subtype/Image/Type/XObject/Width 50>>stream
-H‰Ì×çWSÙ -Ébs-ÀàrØæ:…DÀa@„Ñ -¸=Ʀ¿OO˜š†ò
u¼²þi×Àøô÷oC&^Õ‚ ¡Žƒ†z7_Z0E¡j¨Áñoÿž‡øÑ‚݃s -ÐP¡¼r{¾BÕæª¦}Ö=Ä÷Šïÿ—b>y,ðY-ü}ü³w¾ÀºZhí.¼?þQšêsCaj/™MìW“3û•I¡µ'Ï-Œì»cs ý½}jlàI¡~LÁ¢uVapF
Ž™†ç 8k§&çxªæ¬= Ÿµc““£s -ø“ -Îsø0:?¡i†–®Os–º\ÿômÿ—‰‰¹„öm ïÈøÄ—O*±Ç¸@hÝÈÊo>îèùÔÕÚpÅ„€o}ªr¿týá›Þ¡ÑÑáþ·-°H™EhÊ}cöá¿ßù~`däs_çÓúËðÅÒˆ˜)^¯åñÛ¿vçy×ÇÁ¡ªËknrt€TÄ6"TÅ·ußé«
OÞ¼ÿØß×ý²ùFÙ1cË™âµÈBV§*ŠË¯ßÞÙó¡§ãÙÝÚÒ#»6EùªÂ¯0 jÓŽÃ%Õ
ÛÞt´·=j¨>_髾Šj õK§°…N>áë3òO”Õ66·<k}Ôt½òÔþmñ+|,² -u¿‰f–¾9Y|[Wß0ù†Ôí»öäçïÍÍÞ’¸&ÜßÍÖJïKmÑïZŽÐÎÍwéÊØÉé[Ó6ÇÇ„º;Zs$ÝG¥YOar…vRÏ€ÐHyìºõëâbV†x8‹y,*AçU³,$„% -™L$à±D0ñ#Œ -a±8‡Ãa1h…œ
¨Ã˜!`„‚ FC -̇s0Ô -H‰ìÔA€ DÑáþ—v+ÚÚ)htºþ/í‚ -H‰Ä—ù?ÔûÇ/³ïÃ,Æ0c7û2²kÊM!•R–¢+-“¥R—´ˆJ7…ŠÄE%KÑB¡Ev 3c‰[÷ù¾ßŸË,¡¾ûþa–Çã<ç}Þçõ>ç¼ûí?X:*kֺʵ&¥´EÁ…F>QJl{`ŠÆ`0Xdh4¤´#@k,'ˆ`x‹Œ67ˆ40'’È*••J!-n€=™J×g0Yl°XL†J&ⱈ¨ºº -lí„B;[¥ŸkÀ ‘µ pOX™ÎäðÌB'WO//O7g{¾!“‹Žª4O¢1
ù–Bg‘·_PÈæÍ¡Á¾^®Ö¦\@°h'::ÐEŸÃ·²w÷‡ïˆŽ‰ÙâçélcÆeÒHȾT6…Á‘h,#¡»ohø®=‰É©©)Iûã£~ÚèbcjÈ ¨9ž¬Ç1±qñØ}0-S’“-É<’·=x£“5M‡NT¸)×ÜÞ30|wÒ±ìó…WŠ®^º“™¿=ÐÓÞœœ¬ÜK¤2œ¼Åщ¹…%·Ë++Ên^É;‘¶ÉUÀcшp[ªÅ¸úm‹?œuñFEM}cC]UYQ^fâÎO¡G„[ˆ’àÛ¸D$dœ+.¯mzÞÞÞö¤æÎÕÜ´ø0_'K®>¿2e"pGâñüÒê§]==o;šÞÊ?‘à&à1©U…%PY<H<YXV×Öõ¡¿ÿSwGceQvê®PO;6€]:8]ˆ\àˆS—î6´÷ŒŒ
÷½m©¹™w4~ëF{3ºZè †‘•«DâÉ»/?OŒ½ï¨/+ÈLØæëd®}¥Jpd}®¥ó¦ðýÇ/ÞixùqtR:5Ö÷æIÅåS‰~.0ô•G¢s̽Ãöfüy»¾ããè”\öuà]󃢬¤nÖFpXÇË6nïNÏ+mÿ0:5-Ÿîi}X’›ì.0fPÔŽ†ÎâÛŠBbŸ¹^ÓÖ;<9==5òþÅ£gÄ„ˆlxL5Bº±µ[àÎäìkU-ÝCòiéè‡öº[yéq›=íø,Õ„ ÀÐ]ÀaI®Ükz;ðcŸ^Öÿuáø-„Š„¨$=Cs'ßm ' -ËŸ¼ù<.›–~é{ÕP–Ÿ±$Ä”
´ˆR9,–H70³‡u,¿¬áUß©¯ïdîóv@R¨Nhl¡—x÷Ñý¯tüó›'å…Ƕù8šqT“ބޖghlÚ¹ÒÚïG&Ñßù´âÒ‰ýᾎæ:IƒÀS˜<]‡ÏܨyŽW&èjª¼|êÀöMN†ª2Y:^{PTJnÉöžá HúÛg÷®H€Lœ-5 ]PFŠ„Wµ‚„ -bz -H±¶ô\Zl¨ÈV«—‰{ -B:ö±ãï[çÓãÄ^ÚÅ« ¶/Ó3²/Ÿ^Õß^]¼N›‰Ééùøç×?¯&1+ÿÚÿæñ݂̽ˆx×"†&gf§'ú;Ÿ”_<¾ïwmr×$æf&ºžVž€r7×»1ˆÉA ÞK'µË]“˜ýˆ·@î'„ê¸bvrÈ]yAÖGL
½DI¨_ÕˆD—ð?&$«KJü¹Í¿H\ýbñÖ6w¯—X¬ë$–«OKϰtnmBYáe]«jí‘ÎͯFÀšˆ'냎¸3)»¸ºµwT67?·*RÖ]P©s@¥~?*û6?'~×|©íÁÑ© Ù¾ø0&Ÿ_ -§I(€h «ê6@ÌÿþmjTj¤îªWÑå!È*·¤ûÂüÌèÈ(£Q©)W9ÅU-°©ÍÍÊÀH¬c ¨wƒ•DJvÑý¦Î¾±)™ôë`ÏóG¥çÿ -Bc&EìKËÊ/¾]YU]Uy»¤ +}_d¾rÈ8ÜÙ|‹¯8ú@º$¯°¨äzIQaž$}”ØÇ<×hDw -_k"ÿ°è„ÔIÎÙ³grNe¤ì‹Úêç!D^kjmÙ–ßÚÑÓKdÜþ¤Ô´´#©‡b#Å~"Kø"ÄcÔæ]4ŽHeš -E¾ÁaQ1q»ãb¢¶o
òñp°æsô¡•AnO¦3¹¦Öö®^¾Á›Å[Ä¡Áþ>"¡%ßAS¸P}Ú¢q€ò-öÎn"¯
6xz¸: Íy]¬Øâ…ÃÀˆof%°ÚÛíl-Íx\¶> -q -/GS0 gs -50 0 0 102 495 389 cm -/Im0 Do -Q -
endstream
endobj
50 0 obj
<</CS 11 0 R/I false/K false/S/Transparency/Type/Group>>
endobj
51 0 obj
<</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 50>>/Filter/FlateDecode/Height 102/Intent/RelativeColorimetric/Length 2342/Name/X/Subtype/Image/Type/XObject/Width 50>>stream
-H‰Ä—ù?ÔûÇ/³ïÃ,Æ0c7û2²kÊM!•R–¢+-“¥R—´ˆJ7…ŠÄE%KÑB¡Ev 3c‰[÷ù¾ßŸË,¡¾ûþa–Çã<ç}Þçõ>ç¼ûí?X:*kֺʵ&¥´EÁ…F>QJl{`ŠÆ`0Xdh4¤´#@k,'ˆ`x‹Œ67ˆ40'’È*••J!-n€=™J×g0Yl°XL†J&ⱈ¨ºº -lí„B;[¥ŸkÀ ‘µ pOX™ÎäðÌB'WO//O7g{¾!“‹Žª4O¢1
ù–Bg‘·_PÈæÍ¡Á¾^®Ö¦\@°h'::ÐEŸÃ·²w÷‡ïˆŽ‰ÙâçélcÆeÒHȾT6…Á‘h,#¡»ohø®=‰É©©)Iûã£~ÚèbcjÈ ¨9ž¬Ç1±qñØ}0-S’“-É<’·=x£“5M‡NT¸)×ÜÞ30|wÒ±ìó…WŠ®^º“™¿=ÐÓÞœœ¬ÜK¤2œ¼Åщ¹…%·Ë++Ên^É;‘¶ÉUÀcшp[ªÅ¸úm‹?œuñFEM}cC]UYQ^fâÎO¡G„[ˆ’àÛ¸D$dœ+.¯mzÞÞÞö¤æÎÕÜ´ø0_'K®>¿2e"pGâñüÒê§]==o;šÞÊ?‘à&à1©U…%PY<H<YXV×Öõ¡¿ÿSwGceQvê®PO;6€]:8]ˆ\àˆS—î6´÷ŒŒ
÷½m©¹™w4~ëF{3ºZè †‘•«DâÉ»/?OŒ½ï¨/+ÈLØæëd®}¥Jpd}®¥ó¦ðýÇ/ÞixùqtR:5Ö÷æIÅåS‰~.0ô•G¢s̽Ãöfüy»¾ããè”\öuà]󃢬¤nÖFpXÇË6nïNÏ+mÿ0:5-Ÿîi}X’›ì.0fPÔŽ†ÎâÛŠBbŸ¹^ÓÖ;<9==5òþÅ£gÄ„ˆlxL5Bº±µ[àÎäìkU-ÝCòiéè‡öº[yéq›=íø,Õ„ ÀÐ]ÀaI®Ükz;ðcŸ^Öÿuáø-„Š„¨$=Cs'ßm ' -ËŸ¼ù<.›–~é{ÕP–Ÿ±$Ä”
´ˆR9,–H70³‡u,¿¬áUß©¯ïdîóv@R¨Nhl¡—x÷Ñý¯tüó›'å…Ƕù8šqT“ބޖghlÚ¹ÒÚïG&Ñßù´âÒ‰ýᾎæ:IƒÀS˜<]‡ÏܨyŽW&èjª¼|êÀöMN†ª2Y:^{PTJnÉöžá HúÛg÷®H€Lœ-5 ]PFŠ„Wµ‚„ -bz -H±¶ô\Zl¨ÈV«—‰{ -B:ö±ãï[çÓãÄ^ÚÅ« ¶/Ó3²/Ÿ^Õß^]¼N›‰Ééùøç×?¯&1+ÿÚÿæñ݂̽ˆx×"†&gf§'ú;Ÿ”_<¾ïwmr×$æf&ºžVž€r7×»1ˆÉA ÞK'µË]“˜ýˆ·@î'„ê¸bvrÈ]yAÖGL
½DI¨_ÕˆD—ð?&$«KJü¹Í¿H\ýbñÖ6w¯—X¬ë$–«OKϰtnmBYáe]«jí‘ÎͯFÀšˆ'냎¸3)»¸ºµwT67?·*RÖ]P©s@¥~?*û6?'~×|©íÁÑ© Ù¾ø0&Ÿ_ -§I(€h «ê6@ÌÿþmjTj¤îªWÑå!È*·¤ûÂüÌèÈ(£Q©)W9ÅU-°©ÍÍÊÀH¬c ¨wƒ•DJvÑý¦Î¾±)™ôë`ÏóG¥çÿ -Bc&EìKËÊ/¾]YU]Uy»¤ +}_d¾rÈ8ÜÙ|‹¯8ú@º$¯°¨äzIQaž$}”ØÇ<×hDw -_k"ÿ°è„ÔIÎÙ³grNe¤ì‹Úêç!D^kjmÙ–ßÚÑÓKdÜþ¤Ô´´#©‡b#Å~"Kø"ÄcÔæ]4ŽHeš -E¾ÁaQ1q»ãb¢¶o
òñp°æsô¡•AnO¦3¹¦Öö®^¾Á›Å[Ä¡Áþ>"¡%ßAS¸P}Ú¢q€ò-öÎn"¯
6xz¸: Íy]¬Øâ…ÃÀˆof%°ÚÛíl-Íx\¶> -H‰ìÖŽêV -Lh<²K`Bã'»&4¾I -Óÿ -ÓÉÓÙ‡;¦0}°#“ÙG::1™}œ‰ÄtöQƓ٧MLfŸ`º°¿M/Òt]ÿš^•é²þkz4¦»úbzìoº©;¦WÂΦƒºkz)ìiº¦M/†ÝL§ôÓ«aÓ=0½v0ÑCÓâ]Ó=ezI¼e:Ÿ'M¯‰7LÇó¼éM±Ñt8/™^›Lgó¢éu±Át4¯›Þ¯š.f‹éñ’é\6š^/˜Že³éÅñ´éTÞ1½;ž3ÝÉ{¦·Ç3¦+y×ôþxh:‘L¯¦ÙÅôùé<ö2½G~6ÝÆ~¦7ÉO¦ËØÓô.¹oº‹}Mo“{¦«ØÛô>ùnº‰ýMo”¯¦‹(Lï”ÿ›î¡1½Uþkº†Êô^ùåøƒò‹_”A“gNûñÏs38›Ae§pšÛ6ƒ¨ìÎuÖ`•Í;ßIÏ7ï9ç=Ï9Ûœ÷˜§Œû§Ž'ÿŠgŸGöº`zÂóOÈïìs¿ü€—’ûv9Þ!׻ʜ|µÇå;Ý…Få—Îvèá.5,ÿxÿhG_íbãrÁÆTv5Ll‡±GfþXmLeWrÑÄÞ}pîsáÆTvWNìÝù§gÿoìLþ).ߘÊNoÆTvv+4öÎ+¦'ÿk4v{ã!Óƒ€E»mÉôÜë[§1•ÕJ©ì¤–jlós¦Ç^Ûb©ìŒVkl닦§^Ùz©ìtlLd'³bc*;™%Ûø¬é¡WµhcÛ6=óª–Leç±lc›ž6=òšņì,ņì$–nlËó¦'^ÑÚù”ÂêWXüy—°¡±‹]añç]Áòù”Íû€¬ÿ“{½±ž`ý>iä©»â >à‰mºõ¬é•½dí×=%HàÓ½¿à™*{ï—³Óã4Æ!4Æ4FOd@côDFNdôDFOcäDFOdäDFNdäDFOdäDFNcä4FNcä$FObä4FObô$FOc@bô$Æ$Æ4FOc@b@cô4Æ4FOdô4Æ4FOdäDFOcôDFNdôDFNcäDFOdäDFNdôDFNdäDFNdäDFNdäDFNdäDFNdäDFNdÔn7•9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘¹›Ê¨‰ŒœÈȉŒÚMdÔDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFî¦2j"#'2r"#'2r"#'2j7‘Q9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘»©ŒšÈȉŒœÈȉŒœÈȉŒœÈȉŒÚMdÔDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdän*£&2r"#'2r"#'2r"#'2r"#'2r"#'2j7‘Q9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘»©ŒšÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈ¨ÝDFMdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdä¾F¦2v'2r"#'2r"#'2r"#'2r"#'2r"#'2r"#'2r"#'2r"#'2r"#'2jß{9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘9‘‘¹ï‘©Œ‰ŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈȉŒœÈ¨ÝiLdìKdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNcäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFOdäDFNdäDFNdäDFNdäDFNdäDFNcäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdä4FOdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNdäDFNcäDFNdäDFÎr"#'2r"£vµ¯‘©ŒÝ‰ŒœÈ¨}kLeìMdÔî4¦2vu·1‘±§û‘©ŒýüÐ˜ÌØËω©Œ}ü¶1™ñ¾G‰ ·<Àõü)À -H‰ì—y8ÕéÀçÞæVÄ9ö}—c©c_"»ƒ²%2QY²¥ÒŒ[3’"’†RdIE&×Ò‘’d9öýX²tuÿºïû;‹ƒšgžûܹJ¿Ï_ç÷žóÇ9çù<Ÿïûýæ”Êßþ$ëý=Q¾Hèúû‚ª†òß@çÖ&„o?ùíMô¶÷—Gùü¡ù…¸õÀf2[VC9‡¡èFSm½ÊçU0$]À®-[¶ne```ddÜöqÀ;Œ[·Bã×Ȧ¡¢¡|zÁ@¼ ^ŒÛ˜˜˜1,–ºä‹Å`˜™™€nÐ5¨4
e
«ƒ~1c°¬lìœ\ÜÜ<Þ• gÜÜ\\œìl¬,XE5hhëýÓP>–
ƒ -†øÅÁÉÍÃÇ/ ($,"*&&&¾p**"",$(ÀÇË]ƒªÓ h h¨g((†Á„Qcae‡~ ‰ˆm—”’‘•“WÀá?§ //+#-)!.&"$ÈTƒ¦a˜)¢ ¡ž}õ#
ÛÆ -ãå~IHÉÊã”Tvªªkhjiëèè®EGG[[KS]
¿SE '/+-!.*LãædgEІz†B‹4ILT\RF§¼SMSGÏÀÐØÔÌb¥•ÕÞµXYYî±0751Ú¯«©†WQR€¦‰Ñ8ØX°Ì«<[ïß‹òg9b aÌ0>A(˜âU
]»MÌ-¿³ßçäìzÀí »»»ÇJÀÉA·®.Nûìl÷î171Ô×ÕRÇ«(ÊËHŠ‹òñÀ Q=ƒ÷3T³¯
²bäˆ1aX€a aÛ¥åUT5u
ŒÍ,mì÷»¸y|ï}Ô×ïx@PPpàÄ2ð18((ð¸¿ï±#^žîn.Žö¶{-L
õu4TUå¤%D…øy¸è=C§æ×M11,˜’|B¢2òJx
}#s+Û}În‡¼Žùž8öãÙÈ¨è˜Ø¸sk‰‹‰Žú)"ü‡Ó¡!þǼ=Ý]ím,ÍŒ€hxeI1a~^²g[Ñœ}]P£DŒ‹W@d»´‚2^SÏÐÔÒÖÁù ç¿À§ŒŒ9w>)9%õJZúÕŒŒŒÌ•€“«éiWRS’NLˆ‹Ž;|ÜÇÛÓÍÙÁÖÒÔPOKU'=s“ÃDŸ³õþPþjèòrpó‰IÉ)AÃÌ÷Ú9ºÁ‚BÏDDÇ'&_JËȺ‘{ëö‚»……E€âeàcaáÝ‚üÛ·r;–‘–šœ”è{ÄÓÍÉÞÚÂh—¶š -NFBLˆì0g¨f_4Å9ÉÉ"&ƒÛ¡®kh¶×ÞÙý°O -p³c™‘ -ò°³ 1û⡌J°T‚ÛÈ^ÇØÚñßÉÈó©YyP1˜“Ãã¤p£F™{”ÖÌôirb||lttt„xŸ˜œ$MMÏ@Ùæ¡j´ªQ=÷³ñ‘bçëuO fÏ…‡sßoe¤ƒW -¡¶½C%í€FÚoZmm¬,ÎNà:›¿–çHˆxéé`ŽÔV…ʘ3ãúÉ<¥¡Ê³,lœ|b2PÙ¸ù†Å¥æ–Õ4wáÆ¦ -F^YÛ -ˆÉÞF˜Ú{D&e– ›:qÒ -X”Tû÷Q‡m}[[Yš›ž$Œâú»±u5U¥Eù9™éi)ï߯ÇÅÆÆÄļy^bcãâ“’ß§¥gfç–”VTÕÔ64C¤
&H3 -ð÷÷ó¥ÅÏÏÿe@PpHXDdtlBRJÚǬ¼ÂO_jj[;ºûp# -måg°š}7“€ëj‚Ê,2ÐÓÁÔ -*°ƒ¨ÆvàƒWôÌ¡›"Ž@„U–dHŽ~éƒrwq°±4{ü©¯§sOqWC]]uu»Í{Ú÷õô?1·²±wv{îå—”ú1§°ôsu]sG÷ -7d¤$ÅÅDE® Âo„¯]—”’¹!S鎊:B[WßÐÄÌÊÎÉÕóÅËàð7 )é9EåèºlßÐTgÀÎv÷(?~ü€ËlDÄ6T|ˆõv¶@jÝ‘»ÊËëÿtý?Éu0ÍJ!É›êzOìQ¯Þ¤æWÖwâˆ3K`Tþø ;88DŒDîÃ6a*KrÓßžöû—›£¹ ò6BMð%-).zMHà -ÿåK|¼<<ÜGáááåã»t™ÿª€ 0€íº´¬¼âmuM=$¨4['7”O`XT|rznqEMC[÷Àèøô<™z5P(»ÐÏŽw7W—d&†û¹>5b&.ÈG×ÿÚ¬ŒY)¥¨¡oæè—^TÕÔ
ŸÊí=Pc ¿ö)àd-Ï“C½í
Õå…Yï¢Bü½ÜÍÜ×TWV’—•’¼Ê‰—ç"'û…llçÂÆÆvƒ“ë"7ß%þ« -JwÔÚzMì]ž{„FŧdäúRÛŒí&€«¹úmkÔvÁù)|¦<'9*ÀÈ™‚¤¤ÿŒt1;Á¡1v–åÂEþkÒJsça %5}£S‡§Flwkƒ†X=º4ÿã»ØðÀN€0}„ê-Y)q!À7'û…ó¬çXX˜™™˜Î3˹s¬çmœ\ -Àx ¿Ø0^,,ÌÌL0[¿ô‰™™¸!kˆH‚HCÐvªk뙑m¹8u&$<:î~FNQÎëfkkk+ØÊ|Ùö¢4'5ö· 7[3]5829áÈd(Û„C7Æ/*¨®¿ßþØék ™UM=PÇÞCûŒˆ¡2övz´¿³±ºäIFÒí¨Ë§=ݬ÷“ô´Ôˆò2Râ"‚ü¼Ü(À¶ƒ/ÄkëÖŸÐüë/ƒ}ü Ó†SCÒ8¹ Ò -ªš{F^Ñ-/¢rÝö¢,7=!:ô¬ÏQ2iº²œ$=Äþ¹WQAœmÛ[“_Xœ -òr±ãÄþ©ûêeÈÜLhk"f°4Uµ
öp>á{>âvJÖӪƮÁñX™ láíôHosUAÆÝ«Á>®Ö$-e‚(cb,ÌÌËv^i%Mc+ïàÈ»™…Õ-}c3ïÀÄØÇUÊ;X•õåù™÷n@Œ±·0†MIâãbg]'ö]^‡ÞGgÝLVi×ncû#>f÷2òžÕµ¿ÅVæeaöÕhoKuaÆÝÈ`o+cM%i^Ží,e<tcÂRІ–ÎÞA°+«[_޽~·´F56?3ö²¶4'íÎÕ?ˆ13=
´)ù¹9X·}ObÔnÁ·&ÎLTjQCÏÌÚÙÃ7$2îAvqMKïȺ2—(‹s¯Fû²øÈ ogKC
E)a†²
êaÉÎ#$© n@vò -Œ€>ÆÆ‘1¬Ž¼Ÿƒ5Ôò¼(+9&<èäQ{#Ui1A^(c,Ìß™þH:3v.^A1‚‚ªŽ -³À°[I«šº‡&ßP•ÍŒ²‚Œøˆ@/'²º‚¤û6FùßÐÁ±qJÈ«é™;zž K/¨jcË4c³SÃÝM•™÷¢/x¹Ø1&+!̇ÊvQ~gbØ3©ÌX¶±rpC5“U†0³qñü÷¥ë y
ƒ¯çÑ‘‰+«ÊO?ãéh®§&/!ÈÍÆP¶‘ƒ–l\ârªºû<ÂbÓòªšûÆÖ-¾êj(ÏK -ñ=~È’´bLnJê¦üÄð—bÌàÒÄv¦4„™1ÙÑýôùÈ;iOÊê:Ö•ö6Wæ¥Å†x8ìÓU•àbcœ˜7È–\üb²*»ÍìÜýCcRs+š{¡Q-/¾ì¨+ËI>yÄn¿&QN’cø¦üqEÌðÉaFÔ4ØwÐÍ'0üöƒìÒíýã4eÐþ›*rScBýÝíÌv«ÈŠñ3N̼ô³rò‰ÊuLmú^º™’SÞØ3:Cëc`lb ãEivJLØYo×fº» b‚<?6ÆèïÝBÛ™<bÅ]{Lm\¼Î\¹•ü¸¤öKe#=å9)7/ùµ5Ñ&Dø8壆vXŠH+i‘lÜN]ˆ¾Ÿ]ÖÐ=23O7öfb ½¶äÑý›¡žÎV°*å¥à¨üñ1F{1¾3Q˜ñ‹HîØ©clyè„ÿåIYÅ5mTe”ÅÙéá®ú²Ç÷£/œ:lm¬©$˜ŒZ¶C?,¥5Œ¬\|Î]K|TR×5<=OYûˆ[x3Ñß^[œ•tã’¿‡#ÙHKENBŽÊˆ1ê›éaÆÉ+$+ÓÐÂá¸ßÅèĬ¢š¶—˜²%ÊÔÈκ’¬Äkç|\,
é'&#Ê~ø`¥?,wX8ý™ðGqmÇÐÔeuÝX[MQVbôE_ws
elUnPŒÑŸ‡7¬L%uýýÿe¿Ìÿ¡Þ÷8~=î=-”eÈšAe‹l)FRÉp¤²dZ[mD¡IY -e)ÂQ¨)!¡²$JTvYÆ3côÜ÷ç33tº?Ýsï÷õ|>Ïëóz=_.^áqiys€dMéÝëÑÁ~{í-´Èþ‰Âœsa èÇÃÒŽæsúRrΣꦎÞ&gúûËK‹ðr±µ4ÑÕPAU9o1&88/Ìpeªë[P=ÂþLE.ã5æ$“ŽŽ^õ0'9êLLŠ†Ëæ\< CІåÁ‰·K^¼ýØ3Ì`sÿæ±Ô?Ãü=©F:êÊrÒ¨*ç/ÆøGçÍ̥ˤä”Ô´(TgÿŸ\66ÔýáMeIöµ‹A^ÎÛÍ -<6ßÇÿßeâ’$²¦9Ïeiùkš:z¾Ë&£Ÿ[ëžþ• -ð¿ÙX›À²9.K ©+56ô‡Ä¦”½jý<06 ›æ²Ã…‚Ƕ›h’ùÅ—ü²0<Æw3’‚*¸l;rY\Zþ“—ÍŸz‡Ç'Àe#ýhbæÝˆ>ãG³³ÀX&ޱl¾þÿ"\–â’²Ê -v™vdY@øåô‚²Ú–ξä241›ªå$Ež8èbc¦·JI–(Ì9ŠEP–Šêº¶9z^¼vûAU#–\ð›1ÜÛÑTÃëJÇd–Çø¿x©Àe°1ÏÅß*,¯ƒ@e€ËÐÄ|[Yœ•p>Àc÷Öõ:j -2DaÎøe)¯ªe¼ÙÁíXx|Æýg¯?ð†åw.›9Òû©¹æq~â±-€YùwaüŸqlL¯À ™÷+Ú¾ò'æ×¶†ŠÂ›€eûí-4¡0%ˆÂœ#!ê_´—%ÅŽæ“VP^÷þëËéï\s´¯³ùå“ü´8´+±Çäù›ïYù“~t™!…êr0(âZvqå›Ýßèà2€ÿw¯–öq¥šë¯R& -s΄ž—åÚ
ÖNÞ'¢’sK_¶t
Ð1ôs&Fû»ÞÕ–¤_ÿOÍ÷ÑÒ¬ËÈZFv®>§¢’sV7uô3&Y“Œ‘>À²‡w#=·™êÎæ|Ÿû_ˆú‹Ã²Deù›ÿ¹„¬’}<蟚üÜZW^x3þ\€§“Í‚öجËH -dmcKû½~g¢oä=®}×50Ê—–½y^”vôÀT˜r’⋉(¾xe¹œ¤´JßÜ–æ›^XÑÐÞlzŠ5ŽAæ^Æ•óǽœm(?ðØ‚|žËÄ—“ÕtL¬ K/|Zßöuˆ>ÁbMЇ¾¾¯//H>㳇j¦§¡D"Ø.Ä£~iy²Žé6G/(Ë»k[?޳¦¦Ñ°üÖýáÍó⬫ƒ¼]¨Ãî±—IÊ*©¯5ݺÛ=à|BVqåÛ¨0Ù,À²®–šG9P˜»·˜h“å¥ ö¾Ô¯²ÆpÓŽGï@Y6}ê›@ãÀ°ìh¬zp'1òäï®¶†Zäî±Y—É)¯Ò7Ûîì}"2)çQMKgÿ(“Åf1àBo+‹2.‡Ù÷«…Áj‚ýçB˜ú%dÔ×n´Ùãs&&í¯§
í=ÃL6w ¬¿³¹¦47åÓ¾´_-´É -¤î1Þ·+d®1 ØÒ|ÏĤ”×½‡Âœd³Ë¾¶Õ—çßøãÔAgë
ÀþÒûY8ÈàÛ¯Ô4¶Úé~übbNéËw¨,ÁcS,úà—ÖWeù©±!‡÷ïØl¢£¦HZ.¾À=†þ
ÜiÉ2iyU˜2ÎañEÏ¡0G,6{bæÃÛWÏû»9XQ‘“$¢LÈÂA¶LFQCÏÌ–vèlܢʷhY -€¬ ÿÖå°£n;·¬×UW’•\øÃ…‰¶ŒŒ¢š.`™g`Dâ(Ì®± 6‡ðñͳ{é±Á¾®6ת+ÊQ&\Í™–É–ÝžAQ)yeumxY¢²íëh|Q’}õÂqÇmô4”EÂc|—!øGXFÝãs: -³¾ûÛ8.Ì!ˆçǹIÇ=vZk®$¢LÈš 2}s»½‡Ãâ³Jªš»èhY¢²ìjyYš›uò ‹ùºÕ*rRðéEá=xð°LÓÐÒ~ÿ‘°øÌâÊFHh&›ÃžíÿÔøØÿì!ÕLˆ2!ëÇ sô:qéFÁÓ†=#~Y"¤Åûíµ³0Ô\¹B{LžCŒ÷{¤È:ëqa&å–Ö¶~¤Or8,Æ0\¬,/%*Ès— Ê“ M?Ù¾#aW²Ô´ð©—åÛÊ¢Œøð£nVÆ0,e–-Y„<& -Ï¿ÏÒå$%
(LWßàØôŠ×zG Ê8“ô®æª’ÌøÐC4[ˆ2˜‹ÿõO‘¸–( -¿ŸÈ¼N¢ {ý±—Gý¸,kå$Fz:n3ÕU‡a ì²ðŒ/–IÉÔ4²ÜávìÜÕì‡ÕÀþ(ÊØÌ‘žö†òüëQA»¬Œ4Ud—/%úRXƒ‡QT×3·"»’ý°æÝç!Dý\s¸»Ê25úŒ+Õ\†~Ññ˜ 0e`aZ;yÃÊ/¯oïf°P”
v5WC”=SWM^ -®&271Á;@¥Èªh[íò<Á²>dS“cˆŽ‹3ãÃŽØai@&%! -ÃrVü„…¹Žb·ïp(°USçÀØl”å¥Dº;X®V&-#Ð_HBϰX\Z^míF*íPh|Ödã(ÈØŒ¡/µ¥¹I¨,·®×QC@&R›)̪<ƒ"SòÊêÚº‡l~”UgÄ…øºX›ê¨®Ú¡»‰Žx…BR^mh¹Ó=0òz>LKLd\ þÞ¯+ -Ócƒ}qY*É" )ͦ¢†ž•æ—Qô‚eS8ÊêžÜMºàÀž¢¯¡(#Aô¥p„úD\r…ªŽ©µ‹oH\fIZNaê¯~p,Å‹\¡ˆñ.ˆØóN÷ãÉwŸ (c²§ÐÀìlª¼ŸsÊÛq‹1B¢/…"Ü–€ýúöü/&Ý-0™àp¹læpPþõK'½`YŠbY"á(“V -Cø 0öoqô>só~%zÖw -ÿs þË¡‡÷ÚQÖVÁ²D°¿ÊCK‡ßP”á„£l¬¿ãMEÁõ(@ÿM«”ˆ¾Žp™HÉ“uû‡'Ü)}º„;ÅfÀ7bIŽôصÅDKU°,çûÄÿµ0ûKü›ý2
‡ºÝãøé‘l3Ö±ïadß²ÄÊV3²FÙE‰Tö,i±>vɾ"TH˜“6Y’–#‘S$E$Œ-êyuî{hy®žCó\3ÓU×ù¼óÂÌÿÏ÷óýýnpµ‘Uß«,§êZgïЛÉiâäØË¾{ÍÕ91®xéÿÏKê°nü8ø%tÌ}O¤—5´=[ñ´¼§ƒp!;&ÐÃÊHÖbãÏXdÿ ¥V™ -ï›SÝ|¯ïåøÙpÿƒ[µÅ)>fz`ëG±23¬ÿù62ȲI`+“Õ0´òŠÏ»ßrlr®þ=íåÑ~{Ít$~åûåJª~[áÛ¼Q°‘‘Ž_NÓØÖ;,©¸î6ií'~*²s1î»
5äÄ@‘ŒîÓÃýè##‹/[™’žÅ>¿èØ×£ÓÓ“ãÃO»®WçÆ¸á±¿ò¼\‰d=Ýg~£àIƒDRQ×|¯_tfySG¸xI¯‡`#K‰<äh®§,%62F2Ú:ð7ùö€—]ÃhÛx…&\¾ù ÿÕøÔôÔÄÈóG-uÅ`^Ú|5/2‰Öž -ÐÄ -BÇ“¡±IÒ´ìp³¦(9ÜÇÁ\_UV\€›“
‰`!)@+ÈaÃ×Sà/ΘÉÉ'ŽÑ4²öIÈ¿tã~ÿð—y¹|¿Ô€ÕÉİ -}çD… ÁŒAǘ¬ìœ\Ü<<¼ -ÐÊ -¡2'.ØËÞÜ@CAFRTXP€ŸÀM+ÈÂ¶ŠŸ;Û`—ÓáèŒóà~98únš8çesun\ »•¡¦¼„ ©²ijÀ— ’BHGZ -)2˜1$;ŠWPTBj£,F€ÁÈÉÊl”–’B£Ñ’Ô––‘STÓ1Âí=–WÝÜÙ÷r|jf–89:ØÓÞt!;.ä€#ÎHW]##–”ˆÐÊ - -¤6AÁ!Ç#OÆ%gUÖßììy7³°¸ø~ž8ñúùã»7ê/e%ÇŒ<pìè?_š@...ÎNûVS -´¶0‡æ¦;
t5”1h~.6FŠT=#‚ƒO£ n<>Á§’Îæ—”•WT”Ÿ/+-).*,,(ȧ.…E%¥åU5
×[»zž
ƒS¿ôñã‡EPe#ƒ};n®\ºpþŸÅ…àIòrs²ÏeefÐÈ -gºMW}“´'V…B†ä@+ê˜Øí8™’[Z]{µ‰@ 456Ô_©««¡:µuõ
MÍ·Zï=êx569»°ôñ?>.½Ÿ=ø´»«ív3¡¡¾<ÉåK—.VWUV”ÓÊ -OÌ9_ÓtóNû]@G{[ë––ÛÔ§åNkÛÙ/都Ò4Œ÷ÐAzšˆÒ5¨¨("E†AFÄ‚k»°2²ÂŒe„eí aYEG‘QÀCd%´„%„„ôÐq¿ïÞ ¸ê!ç,yþŽææò{Ê›-.Ã×’š©„±¾>@E§RHµø²bl~^.x’¬¬×¯33ÒÓRÅè -òQƒA§w -ªn±~ -éK£¥¾,÷ÕƒÈÐÀý›ÝVb¬Ìu5T@˜Áe6jg~Д†&sæ¦ôؼÛ70äbäß^dæ—V“Z;X¼®^IMM
ö÷¾$°Iw®†Ý³ÉÍq‘ù=-f"t&ºÆdd†nJÒ”N‹¸ùàÉï9Øò2ÂXOŸ$Ȧ¦¾°;šëÊòRݼrúèžÍîNö¶¦†ºšªÓä„Àç0CªÄØðM¹ -mÊ3aÑ÷¿Ì|[‚¯oj£1&lJ -½/;©äªâœ—o\Øç³ÞÅccn¬¯>Zg¢U ¿†ŽÒ”k<¶|çrñúíøgio -ËkHÍí¶„±)-a”Ñ[ð…Y/Þû9ðÐ/·Uöó-ÍŒ¾‚)÷ùÎD«¬15-=pSb–9»{ƒ¦ü 6eRJvA)ØÔÖÁäð%ŒMi
"QÆcÑšë+°Y/ãoE„?²gëú5+Í›cb £¡ª:ó3a†2¦¨¬bÌÂvÉ*×
ÈM)lÊâÊ:r“Ít÷"ŒI ›ªB£ŒÛÙN”e'?޼|ìÐNï¯W/[hmn¬§>| -RÖßÛÓÅcw‚0«(ÊM{û:8 -)“—…ëd2ò ÈÌæ-sÝ´ïØ™ˆÛ“³±°)Û›RÒ”
k®P™H˜µ6ÖáKÞ êRHàá^®+í¬Í -´¥¬‚ж‘ÆÉs§pXÌã”7Eøú‘M)AL¢‘fŒÿ²_çßPÿ{ -R„©O™„¼†¡Õ.¿°KY…•z>0ÆÁôçýf˜²Ž¦êÒ?2.†Úkc¢¥(M$ O;d—o=ÓÑûydõ ÿƒX - ƒ8 -C6Œ!+!§œ;êaI³-`†-!¥•t6Ø{=—B.©m¡Ð†ÄA“×eKMInJt‡™¶¢4‘&2~¢´¢¶™GPtrnI
†ì ƒðÇD6@y_ó$'9:ÈC&%<™°”¢–év÷À¨äœ'5ï?
-¹¤¶…Bdî0d#ƒÝmõOóÓâŽ{ïØ¬«"#2?QZQÛÌÎ#(:%·¤CöAx›ø>Ρu·Ö•å]=æå¸i2I„0™°†Ì=(:9çIÍûO€Â¥µ¶”œ¼ßa£Ž‰Hà]¼h2-ÓíîQ€â8Ù0ÒR[BN9wÔÓ~ƒŽ’4‘°![0ÙJ„Ì-0*)çq5 ƒ8 -!û‚!«)ÉM‰ò°3ÓV”&ò³Efbë™tAÆD6@y_ó$'9:ÈC&%<†LÓÄÖ5 2!kžDÊ \±}b"‹ -tßnªÅÙ2IMWÿˆÄ;ůš»ú„¿iÈ’¢Ý²•ó [cl³Ï?üÙ ƒp÷YõE¶Cf´mï‘ð„ÛªÞ!dc_„³)dï&E¸Ùš°AÆ…I¬Xm´uÏá³Wo=|Ùô±!ûÈ |ÍA¦É™B¶Þz÷¡3W²¼hêì£2w“Èš²ÄÈ -rRãÂ<˜:…LÒÕV•ÌÍ—Íå@20&D²~©¸UX!ÈN‰
õwg2h²>J¦õG2{7ßHÆ%ûF$««(ÊNŽÙ¹aÅÂY4ŠáÉôMÌ&ËDÉš&ƒe -øÜhMÉf/Z¹q×Án<xÑÞE$‚d@#üd$#S+;gïàÈÄŒüÒšÆö.Ù€’?“5£d™Üè"™¹ºdTK['¯ ˆ„ô¼’ê†6‰LÉ -Áô‹KÍT -EbH°Œ$û¢Lv>)j‡ëUÉ´P2}2…Æ`ºû‡Æ¦d*„bi?$P²ï_Q²7Ïî];—t`ûz"Ù•É)´YWlØ“œ]TQG$ûÉ€fÃÃ?P²ž÷MOï^={lÿ¶u.ógLAÉtT%3± ’±c’³ŠÊëZĽàIÖ×ÓÑôäΕ3G÷m]»%3VŸÌa¹›ÃË*DÉ:!À¢LÖÝñúñí˧ìݲfé¼éfê“Y£d,É -™¤«
ÉÀßB$ë—Š[…‚ì”ØPw&ƒF!ë£dZ*’™ÉB¢¹™(Y3$xP²oD²ºŠ¢ìä˜V,œE£ªL¦g8IU2X4IÖ+n©+/ÊJŽaÉ,LÔ'›iïêu<óúýçÍ>A2€C™¬%+ÌâqØ~ËFO¶ÀÕgGTÒye²/`ø-ŸÇa¡dÖj“M ’¹¬ß~ éܵ{ÏÞ d_¿C2 ~2”lÊŒù.ë¶í?vöêݧMï{P²hò{2.‡å;j2c”lÙÚûŽž¹rçISGO$þ›ÌÍ~´dfÓç-]³eï‘Ó—o?~ÝÑ
É -øÜèÑ’ië’È:ƒé—š#¨ŠÄàÀM¦…’é“)4ÓÝ?46%[P!lKû!Ðìg²f”,“B$3W›ÌBû‡]:Šùà -’¹H²º_$Ù»'×ÏLX=?ÌŸIÖÉ€ƒá7IVú9ûñµÓ¶š7ÙP¯.$™K²±TŽd`6c²ÊÒ‚ìGWOíߺrî¤Q$™=[2Q ’/¦d%o^IMÚ;gâÈnNH41ɪ+K>½yqrßæ³'ŒÐÓÉÞÖÚª%’%Ædß¿~|}ÿò‰½›–Ï?¼ÎlÛ Pch`’翺wéøžËfŽÖÉ€.’¬¶º¢8ÿåÝ‹ÇvoX:cìP¯îÚÛ˜‘ìæ3$³0ɪ*Š>¼¸sáè®õK¦‡éÛÍÉ€&CC}mUyQÞóÛçì\·xZˆ_WG’E¦dú¼¬[é‡w¬]4uŒ¯§«P`Óºk²xyÊ9$31É~–ëßgiÓÕ‰kNíëé"´%ÉZ ÐA’Õ0ÉtZZ)‹‰ -öñpÚý=YW&Yt¼<™$ËA20‡1Y™>W—©Q)e&™³Ãß’YÛul*–+S²B’,M¥J"ƒ¼9’õ„GÇmO>{ãiΗoHÜ%KQHÅ$™;G²Ááâ™’ý@2àÄ/Y;&™ØüÕ Ï\òŽ$ûU‡dÀ¡q2¹TÁ–ÌŠ$ëÒkÿäy«¶8}íqöçR’ì7’»?“ŠX“Ù“d£&Í]¹uÿ©«²J+‘8ñMæä6päÄ9±[’R¯<|[P‚dÀG²–VÖ¶öN=Œ˜0{Åæ}'3¼ùTRYdÀ…W²6¶:÷è?|ü¬å›öž¸|ÿõǯߑ8ñOÖoظ™Ë6î9~éÞ«üb&Y’+~ÉlÚwêî5t쌥v»x÷e~qEu-’¾É»õ:}Éú]G/Üyñ¡¨¢ -É€ÏdG×>~!Ó¯ÛyäüíçyEåUµõHìø$kÕÚF tõô3uÑÚ‡Óoeå鑸ý›ìÙM®d-H²¶¡‹§ïè)×$ªÓµYïõå?‘¸ðLf'tñð ŽŠ‘)ÕŽIVƒdÀo2g&™D¦Ti2u¹ú2$Nÿ%;—"7+™wP¤DªP¥‘d…HÜø's'ÉÄRE -’™þI–C’%Ë㣙d]‘h24•¬£5{²±TŽd`&S²o_ržÞ8›¼=.:<@Ô›+™(É€c²¦d‡â„F2 ‹$«ûE’½{rýÌÁ„ÕóÃü™díè1~“d¥Ÿ³_;}`Ûªy“ýõêB’Y!ÐbLVYZýèê©ý[WÎ4Š$³G2 È”¬¤àíÃ+©I[bçL9ÐÍ É€&&Yueɧ72NîÛ¼bö„z:ÙÛZ[µD2 Ęìûׯï_>±wÓòYã‡÷ïѹƒm$j
L²âüW÷.ß³qÙÌqÃú!ÐE’ÕVW翼{ñØî
KgŒêÕ½S{$z˜dUE^ܹpt×ú%ÓC‡ôíæˆd@“¡¡¾¶ª¼(ïùíóGv®[<-į«£ -É€&ÙÏrýû,mº:qÍÂ)£}=]„‚¶$ÙÿC2h’¬†I¦ÓjÔJYLT°‡‹ÐÉ€"c²2}®.S£RÊ$L2g3“Ý|†d`S²B’,M¥J"ƒ¼‘(k”,E!“dîHt57Y¼<å’Y'“KÅHÔý™,PÄ•¬+“,:^žL’å pã™ÌÚ®cSɰX4#YoQ@xtÜöä³7žæ|ù†dÀ¥9É„/ˆK8dJöÉ€ßdí˜dþaóW'<sýÉ;’ìW’+~ɬH².½ùOž·jÛÓ×g.%É~#°áÌž$5iîÊûO]}”]PZ‰dÀ2'·#'Ήݒ”zåáÛ‚$.¼’µ´²¶µwê9`Ä„Ù+6ï;™ñàͧ’Êj$v<“µ±íйGÿáãg-ß´÷Äåû¯?~ýŽdÀ¡9Éú
7sÙÆ=Ç/Ý{•_Ì$k@2`Á7™MûNݽ†Ž±tÃîcï¾Ì/®¨®E2`Å?™c·¾CB§/Y¿ëè…;/>UT!°ãLàèÚÇ/dÚâu;œ¿ý<¯¨¼ª¶É€
¿dZÛ„®ž¾c¦.Z»ãpú¬<=’>ÉZdmBOßÑS®IT§k³ÞëË"°ãÌNèâá#Sª5Z“¬É€ÿdÎL2‰L©ÒdêrõeHš•Ì;(R"U¨ÒH²B$.ÍIæN’‰¥Š$³ü›ìÙM$if²±TŽd`žf%"˜ï¿dçRäñH€d`qÿ$Ë!É’åñÑL²®H4šJÖÑÎÉ€S²o_ržÞ8›¼=.:<@ÔÉ€*c²¦d‡â„F2 ‹$«ûE’½{rýÌÁ„ÕóÃü™díè1~“d¥Ÿ³_;}`Ûªy“ýõêB’Y!ÐbLVYZýèê©ý[WÎ4Š$³G2 È”¬¤àíÃ+©I[bçL9ÐÍ É€&&Yueɧ72NîÛ¼bö„z:ÙÛZ[µD2 Ęìûׯï_>±wÓòYã‡÷ïѹƒm$j
L²âüW÷.ß³qÙÌqÃú!ÐE’ÕVW翼{ñØî
KgŒêÕ½S{$z˜dUE^ܹpt×ú%ÓC‡ôíæˆd@“¡¡¾¶ª¼(ïùíóGv®[<-į«£ -É€&ÙÏrýû,mº:qÍÂ)£}=]„‚¶$YÇšƒ$«a’é´µRìãá"´C2 Ș¬LŸ«ËÔ¨”2 “ÌÙÉ€&S²B’,M¥J"ƒ¼‘(k”,E!“dîHt!X\ãdr©8É€º?“Š(C2°8$‹C2°8$‹C2°8$‹C2°8$û»ôþSóãp|DJ‡8Ê܉\æRîÌ5wcîå:ÆÜ -c“ÊeŒ¹¥0ÆÜ‹aîr¿—{.‰pJQ¢}ßïú~÷5«÷ûœ³^û|~x>þƒçö„8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8Û&kÚÉ`1['ëïÂd°“AœM“ÕV&ó g2X¶ɚuà©Lfb2è°a2£›:ÙøÀЈÈè˜W¦4&ƒ6ë'3Ý<¼:
œ0oUDTt¬:Y6“A‹U“Q&s0Ý=¼:š8õŽ¨Ë±ñ¦´L&ƒ&+'³+î`pq¯Ó¼ËàIÖì<våA|“A‡õ“•r©\·E×!“®ÝuüêÃ×Ié™Ù¿™l˜¬\•z-»
²hÝî×%$§g14Y=™céòUë·ê>lêâõ{N^œü%ë'“A‹M“5hÝcø´%öžºñäÍGu23“¡`ÖNfïèìZa›ž#¦/ݸïôͧoS2˜Ú¬›¬¨}Ig×êÚö9cÙ¦ýgn=KLÉøÎdÐdÃdj4n×{ÔÌ囜½—˜ÊdÐaýde”ɼûŒžµbËÁswâÞ¥~e2h³r²bÊdk6ñî;fvÐÖCçï>ÿI™,‡É ÁêÉœÔÉ||ÇÎ ÚvøÂ½Êd?~1´Ø4™§ï¸9+Ãò&ûÆdÐfËdµ<Û÷778ìÈÅû/>|f2è°v²Neó›ìŸÎÀ¿Ùß“uhª;Y%u2¿¹ÁÛ•É^2tý9YH€_&C¡³y²€àíG™–øc²ð -Wîd)oŸÞ<½oãÒé#z¶iXÍÕÙÑ^k²ÚÊdþ!áL‹¨“}ÏHI|vëÌþMËfŒìÕ¶QuWç’övE5&kÖq€`hx¤2™‰É 'o²ÔĸÛgl^>sTïvkTМÌè¦N6>04"2:æ•)É #w²¯©ïâîœ;¸eŬÑ}¼•ÉÊhLf0ºyxu8aÞªˆ¨èXu²l&ƒ&³9G™ìÓûçwÏÚ4{L_ï&5+*“Ëg²"Êd£»‡WçAç¯Þu96Þ”–ÉdЦLöë‡2Ù‹{oš3Ö×GÌ©€ÉìŠ;\Üë4ï2xÒ‚5;]yŸÄdГ;Ù·¼ÉÂVÎçëã©=Y)—Êu[t2yáÚ]ǯ>|”ž™ý›É %o²Ï^Ü¿x$,xî¸~í=kiOV®J½–݆NY´n÷‰k’Ó³˜:ò›¬¬S‰‚&s,]¾jýV݇M]¼~ÏÉë’¿dýd2hûïd/•ɶÏõS'«¤3YƒÖ=†O[²aï©OÞ|T'33´X7™½£³kµ†mzŽ˜¾tã¾Ó7Ÿ¾MÉ`2èùÿdG·hOVÔkõFm{œ±lÓþ3·ž%¦d|g2è°z² -5·ë=jæòÍÎÞŽKLe2è²v²2ÊdÞ}FÏZ±åà¹;qïR¿2ôüo²˜KLVL™¬bÍ&Þ}ÇÌÚzèüÝçï?)“å04Y9™“:™ïØ9AÛ_¸÷B™ìÇ/&ƒ6&óôñ7geXÞdߘz¬Ÿ¬–gû~ãæ‡¹xÿŇÏL]ÖMV©l¥|&û§#ðïfÛd~sƒ·+“½d2XÀ†Éš2¬ò÷dšêOÖ¡¿_@Èö£—˜ùs² -žÌÞÑÙµZÃ6=GL_ºqßé›Oߦd0tåN–‘òöéÍÓû6.>¢g›†Õ\í󙬨}Ig×êÚö9cÙ¦ýgn=KLÉøÎdУNö=#%ñÙ3û7-›1²WÛFÕ]KÚÛͲ -5·ë=jæòÍÎÞŽKLe2èË›,51îöÙ›—ÏÕ»]ã -ž¬Œ2™wŸÑ³Vl9xîNܻԯL]¹“}M}wçÜÁ-+fîãLV&ÿÉŠ)“U¬Ùħï˜ÙA[¿ûüý'e²&ƒ6³9G™ìÓûçwÏÚ4{L_ï&5+*“Ëw2'e2Oß±ÿa¿þé¾â8ާs¿_×u/®Ë¸s/eÜ^WuTô^Ú©ãÒLÓÒpÝ®3å^ISieZ_–¥]Ûà®é–ÆX¹ºX›nÆ%[LŒkë–Rt³öv1ELÅ—Òdç|¨¤s?÷£?.y?þ†gÞçuôõ&³eE¶´‘ -(²•%™ÅÜR_¡ÏÒ¨qd\²Èb_e”F§¯4nDÏ% €žK"²Q‹ÙT_©×iÔJòȘ\Š,Yg¨2¶v‘¡M†NdH¡<ž®Ï%ŽÌˆ#‹RúÚ,$*ùèó‘áÊ -bj¹ÖxõrÍù3'ò2Ą˥b#Ófdt6Oè!Û±ûuMú;ÇO–}ðÑÇ5uõÆO°Ïh¬«¾t®âtq^Vjœj§¿7ÚýLô¹´ËÑÙ]²'6%ýX^щSïŸ-¯¬€JeEyYiIqANfjBt˜\†^K6ƒ¶i’¡åï@crœ\=¥A¡ªØ¤ƒoçä½WX¤7Å -?o‘3—ec÷¯2–£@äå¹/NóÖ¡ôm–Nw -º,mæ‘é) 1ª0…¿D,ćló$[{/Ñ)º{û*Ã"£cÞˆ3I£I€’&)1!nÿ^UøN¹ŸÄÃ…¿vÈlDFœ2.ªìeŸíAÁ¡a‘{Ôê( -0 -q -/GS0 gs -613 0 0 588 94 7 cm -/Im0 Do -Q -
endstream
endobj
56 0 obj
<</CS 11 0 R/I false/K false/S/Transparency/Type/Group>>
endobj
57 0 obj
<</BitsPerComponent 8/ColorSpace/DeviceGray/DecodeParms<</BitsPerComponent 4/Colors 1/Columns 613>>/Filter/FlateDecode/Height 588/Intent/RelativeColorimetric/Length 26318/Name/X/Subtype/Image/Type/XObject/Width 613>>stream
-H‰ì—y8ÕéÀçÞæVÄ9ö}—c©c_"»ƒ²%2QY²¥ÒŒ[3’"’†RdIE&×Ò‘’d9öýX²tuÿºïû;‹ƒšgžûܹJ¿Ï_ç÷žóÇ9çù<Ÿïûýæ”Êßþ$ëý=Q¾Hèúû‚ª†òß@çÖ&„o?ùíMô¶÷—Gùü¡ù…¸õÀf2[VC9‡¡èFSm½ÊçU0$]À®-[¶ne```ddÜöqÀ;Œ[·Bã×Ȧ¡¢¡|zÁ@¼ ^ŒÛ˜˜˜1,–ºä‹Å`˜™™€nÐ5¨4
e
«ƒ~1c°¬lìœ\ÜÜ<Þ• gÜÜ\\œìl¬,XE5hhëýÓP>–
ƒ -†øÅÁÉÍÃÇ/ ($,"*&&&¾p**"",$(ÀÇË]ƒªÓ h h¨g((†Á„Qcae‡~ ‰ˆm—”’‘•“WÀá?§ //+#-)!.&"$ÈTƒ¦a˜)¢ ¡ž}õ#
ÛÆ -ãå~IHÉÊã”Tvªªkhjiëèè®EGG[[KS]
¿SE '/+-!.*LãædgEІz†B‹4ILT\RF§¼SMSGÏÀÐØÔÌb¥•ÕÞµXYYî±0751Ú¯«©†WQR€¦‰Ñ8ØX°Ì«<[ïß‹òg9b aÌ0>A(˜âU
]»MÌ-¿³ßçäìzÀí »»»ÇJÀÉA·®.Nûìl÷î171Ô×ÕRÇ«(ÊËHŠ‹òñÀ Q=ƒ÷3T³¯
²bäˆ1aX€a aÛ¥åUT5u
ŒÍ,mì÷»¸y|ï}Ô×ïx@PPpàÄ2ð18((ð¸¿ï±#^žîn.Žö¶{-L
õu4TUå¤%D…øy¸è=C§æ×M11,˜’|B¢2òJx
}#s+Û}În‡¼Žùž8öãÙÈ¨è˜Ø¸sk‰‹‰Žú)"ü‡Ó¡!þǼ=Ý]ím,ÍŒ€hxeI1a~^²g[Ñœ}]P£DŒ‹W@d»´‚2^SÏÐÔÒÖÁù ç¿À§ŒŒ9w>)9%õJZúÕŒŒŒÌ•€“«éiWRS’NLˆ‹Ž;|ÜÇÛÓÍÙÁÖÒÔPOKU'=s“ÃDŸ³õþPþjèòrpó‰IÉ)AÃÌ÷Ú9ºÁ‚BÏDDÇ'&_JËȺ‘{ëö‚»……E€âeàcaáÝ‚üÛ·r;–‘–šœ”è{ÄÓÍÉÞÚÂh—¶š -NFBLˆì0g¨f_4Å9ÉÉ"&ƒÛ¡®kh¶×ÞÙý°O -p³c™‘ -ò°³ 1û⡌J°T‚ÛÈ^ÇØÚñßÉÈó©YyP1˜“Ãã¤p£F™{”ÖÌôirb||lttt„xŸ˜œ$MMÏ@Ùæ¡j´ªQ=÷³ñ‘bçëuO fÏ…‡sßoe¤ƒW -¡¶½C%í€FÚoZmm¬,ÎNà:›¿–çHˆxéé`ŽÔV…ʘ3ãúÉ<¥¡Ê³,lœ|b2PÙ¸ù†Å¥æ–Õ4wáÆ¦ -F^YÛ -ˆÉÞF˜Ú{D&e– ›:qÒ -X”Tû÷Q‡m}[[Yš›ž$Œâú»±u5U¥Eù9™éi)ï߯ÇÅÆÆÄļy^bcãâ“’ß§¥gfç–”VTÕÔ64C¤
&H3 -ð÷÷ó¥ÅÏÏÿe@PpHXDdtlBRJÚǬ¼ÂO_jj[;ºûp# -måg°š}7“€ëj‚Ê,2ÐÓÁÔ -*°ƒ¨ÆvàƒWôÌ¡›"Ž@„U–dHŽ~éƒrwq°±4{ü©¯§sOqWC]]uu»Í{Ú÷õô?1·²±wv{îå—”ú1§°ôsu]sG÷ -7d¤$ÅÅDE® Âo„¯]—”’¹!S鎊:B[WßÐÄÌÊÎÉÕóÅËàð7 )é9EåèºlßÐTgÀÎv÷(?~ü€ËlDÄ6T|ˆõv¶@jÝ‘»ÊËëÿtý?Éu0ÍJ!É›êzOìQ¯Þ¤æWÖwâˆ3K`Tþø ;88DŒDîÃ6a*KrÓßžöû—›£¹ ò6BMð%-).zMHà -ÿåK|¼<<ÜGáááåã»t™ÿª€ 0€íº´¬¼âmuM=$¨4['7”O`XT|rznqEMC[÷Àèøô<™z5P(»ÐÏŽw7W—d&†û¹>5b&.ÈG×ÿÚ¬ŒY)¥¨¡oæè—^TÕÔ
ŸÊí=Pc ¿ö)àd-Ï“C½í
Õå…Yï¢Bü½ÜÍÜ×TWV’—•’¼Ê‰—ç"'û…llçÂÆÆvƒ“ë"7ß%þ« -JwÔÚzMì]ž{„FŧdäúRÛŒí&€«¹úmkÔvÁù)|¦<'9*ÀÈ™‚¤¤ÿŒt1;Á¡1v–åÂEþkÒJsça %5}£S‡§Flwkƒ†X=º4ÿã»ØðÀN€0}„ê-Y)q!À7'û…ó¬çXX˜™™˜Î3˹s¬çmœ\ -Àx ¿Ø0^,,ÌÌL0[¿ô‰™™¸!kˆH‚HCÐvªk뙑m¹8u&$<:î~FNQÎëfkkk+ØÊ|Ùö¢4'5ö· 7[3]5829áÈd(Û„C7Æ/*¨®¿ßþØék ™UM=PÇÞCûŒˆ¡2övz´¿³±ºäIFÒí¨Ë§=ݬ÷“ô´Ôˆò2Râ"‚ü¼Ü(À¶ƒ/ÄkëÖŸÐüë/ƒ}ü Ó†SCÒ8¹ Ò -ªš{F^Ñ-/¢rÝö¢,7=!:ô¬ÏQ2iº²œ$=Äþ¹WQAœmÛ[“_Xœ -òr±ãÄþ©ûêeÈÜLhk"f°4Uµ
öp>á{>âvJÖӪƮÁñX™ láíôHosUAÆÝ«Á>®Ö$-e‚(cb,ÌÌËv^i%Mc+ïàÈ»™…Õ-}c3ïÀÄØÇUÊ;X•õåù™÷n@Œ±·0†MIâãbg]'ö]^‡ÞGgÝLVi×ncû#>f÷2òžÕµ¿ÅVæeaöÕhoKuaÆÝÈ`o+cM%i^Ží,e<tcÂRІ–ÎÞA°+«[_޽~·´F56?3ö²¶4'íÎÕ?ˆ13=
´)ù¹9X·}ObÔnÁ·&ÎLTjQCÏÌÚÙÃ7$2îAvqMKïȺ2—(‹s¯Fû²øÈ ogKC
E)a†²
êaÉÎ#$© n@vò -Œ€>ÆÆ‘1¬Ž¼Ÿƒ5Ôò¼(+9&<èäQ{#Ui1A^(c,Ìß™þH:3v.^A1‚‚ªŽ -³À°[I«šº‡&ßP•ÍŒ²‚Œøˆ@/'²º‚¤û6FùßÐÁ±qJÈ«é™;zž K/¨jcË4c³SÃÝM•™÷¢/x¹Ø1&+!̇ÊvQ~gbØ3©ÌX¶±rpC5“U†0³qñü÷¥ë y
ƒ¯çÑ‘‰+«ÊO?ãéh®§&/!ÈÍÆP¶‘ƒ–l\ârªºû<ÂbÓòªšûÆÖ-¾êj(ÏK -ñ=~È’´bLnJê¦üÄð—bÌàÒÄv¦4„™1ÙÑýôùÈ;iOÊê:Ö•ö6Wæ¥Å†x8ìÓU•àbcœ˜7È–\üb²*»ÍìÜýCcRs+š{¡Q-/¾ì¨+ËI>yÄn¿&QN’cø¦üqEÌðÉaFÔ4ØwÐÍ'0üöƒìÒíýã4eÐþ›*rScBýÝíÌv«ÈŠñ3N̼ô³rò‰ÊuLmú^º™’SÞØ3:Cëc`lb ãEivJLØYo×fº» b‚<?6ÆèïÝBÛ™<bÅ]{Lm\¼Î\¹•ü¸¤öKe#=å9)7/ùµ5Ñ&Dø8壆vXŠH+i‘lÜN]ˆ¾Ÿ]ÖÐ=23O7öfb ½¶äÑý›¡žÎV°*å¥à¨üñ1F{1¾3Q˜ñ‹HîØ©clyè„ÿåIYÅ5mTe”ÅÙéá®ú²Ç÷£/œ:lm¬©$˜ŒZ¶C?,¥5Œ¬\|Î]K|TR×5<=OYûˆ[x3Ñß^[œ•tã’¿‡#ÙHKENBŽÊˆ1ê›éaÆÉ+$+ÓÐÂá¸ßÅèĬ¢š¶—˜²%ÊÔÈκ’¬Äkç|\,
é'&#Ê~ø`¥?,wX8ý™ðGqmÇÐÔeuÝX[MQVbôE_ws
elUnPŒÑŸ‡7¬L%uýýÿe¿Ìÿ¡Þ÷8~=î=-”eÈšAe‹l)FRÉp¤²dZ[mD¡IY -e)ÂQ¨)!¡²$JTvYÆ3côÜ÷ç33tº?Ýsï÷õ|>Ïëóz=_.^áqiys€dMéÝëÑÁ~{í-´Èþ‰Âœsa èÇÃÒŽæsúRrΣꦎÞ&gúûËK‹ðr±µ4ÑÕPAU9o1&88/Ìpeªë[P=ÂþLE.ã5æ$“ŽŽ^õ0'9êLLŠ†Ëæ\< CІåÁ‰·K^¼ýØ3Ì`sÿæ±Ô?Ãü=©F:êÊrÒ¨*ç/ÆøGçÍ̥ˤä”Ô´(TgÿŸ\66ÔýáMeIöµ‹A^ÎÛÍ -<6ßÇÿßeâ’$²¦9Ïeiùkš:z¾Ë&£Ÿ[ëžþ• -ð¿ÙX›À²9.K ©+56ô‡Ä¦”½jý<06 ›æ²Ã…‚Ƕ›h’ùÅ—ü²0<Æw3’‚*¸l;rY\Zþ“—ÍŸz‡Ç'Àe#ýhbæÝˆ>ãG³³ÀX&ޱl¾þÿ"\–â’²Ê -v™vdY@øåô‚²Ú–ξä241›ªå$Ež8èbc¦·JI–(Ì9ŠEP–Šêº¶9z^¼vûAU#–\ð›1ÜÛÑTÃëJÇd–Çø¿x©Àe°1ÏÅß*,¯ƒ@e€ËÐÄ|[Yœ•p>Àc÷Öõ:j -2DaÎøe)¯ªe¼ÙÁíXx|Æýg¯?ð†åw.›9Òû©¹æq~â±-€YùwaüŸqlL¯À ™÷+Ú¾ò'æ×¶†ŠÂ›€eûí-4¡0%ˆÂœ#!ê_´—%ÅŽæ“VP^÷þëËéï\s´¯³ùå“ü´8´+±Çäù›ïYù“~t™!…êr0(âZvqå›Ýßèà2€ÿw¯–öq¥šë¯R& -s΄ž—åÚ
ÖNÞ'¢’sK_¶t
Ð1ôs&Fû»ÞÕ–¤_ÿOÍ÷ÑÒ¬ËÈZFv®>§¢’sV7uô3&Y“Œ‘>À²‡w#=·™êÎæ|Ÿû_ˆú‹Ã²Deù›ÿ¹„¬’}<蟚üÜZW^x3þ\€§“Í‚öجËH -dmcKû½~g¢oä=®}×50Ê—–½y^”vôÀT˜r’⋉(¾xe¹œ¤´JßÜ–æ›^XÑÐÞlzŠ5ŽAæ^Æ•óǽœm(?ðØ‚|žËÄ—“ÕtL¬ K/|Zßöuˆ>ÁbMЇ¾¾¯//H>㳇j¦§¡D"Ø.Ä£~iy²Žé6G/(Ë»k[?޳¦¦Ñ°üÖýáÍó⬫ƒ¼]¨Ãî±—IÊ*©¯5ݺÛ=à|BVqåÛ¨0Ù,À²®–šG9P˜»·˜h“å¥ ö¾Ô¯²ÆpÓŽGï@Y6}ê›@ãÀ°ìh¬zp'1òäï®¶†Zäî±Y—É)¯Ò7Ûîì}"2)çQMKgÿ(“Åf1àBo+‹2.‡Ù÷«…Áj‚ýçB˜ú%dÔ×n´Ùãs&&í¯§
í=ÃL6w ¬¿³¹¦47åÓ¾´_-´É -¤î1Þ·+d®1 ØÒ|ÏĤ”×½‡Âœd³Ë¾¶Õ—çßøãÔAgë
ÀþÒûY8ÈàÛ¯Ô4¶Úé~übbNéËw¨,ÁcS,úà—ÖWeù©±!‡÷ïØl¢£¦HZ.¾À=†þ
ÜiÉ2iyU˜2ÎañEÏ¡0G,6{bæÃÛWÏû»9XQ‘“$¢LÈÂA¶LFQCÏÌ–vèlܢʷhY -€¬ ÿÖå°£n;·¬×UW’•\øÃ…‰¶ŒŒ¢š.`™g`Dâ(Ì®± 6‡ðñͳ{é±Á¾®6ת+ÊQ&\Í™–É–ÝžAQ)yeumxY¢²íëh|Q’}õÂqÇmô4”EÂc|—!øGXFÝãs: -³¾ûÛ8.Ì!ˆçǹIÇ=vZk®$¢LÈš 2}s»½‡Ãâ³Jªš»èhY¢²ìjyYš›uò ‹ùºÕ*rRðéEá=xð°LÓÐÒ~ÿ‘°øÌâÊFHh&›ÃžíÿÔøØÿì!ÕLˆ2!ëÇ sô:qéFÁÓ†=#~Y"¤Åûíµ³0Ô\¹B{LžCŒ÷{¤È:ëqa&å–Ö¶~¤Or8,Æ0\¬,/%*Ès— Ê“ M?Ù¾#aW²Ô´ð©—åÛÊ¢Œøð£nVÆ0,e–-Y„<& -Ï¿ÏÒå$%
(LWßàØôŠ×zG Ê8“ô®æª’ÌøÐC4[ˆ2˜‹ÿõO‘¸–( -¿ŸÈ¼N¢ {ý±—Gý¸,kå$Fz:n3ÕU‡a ì²ðŒ/–IÉÔ4²ÜávìÜÕì‡ÕÀþ(ÊØÌ‘žö†òüëQA»¬Œ4Ud—/%úRXƒ‡QT×3·"»’ý°æÝç!Dý\s¸»Ê25úŒ+Õ\†~Ññ˜ 0e`aZ;yÃÊ/¯oïf°P”
v5WC”=SWM^ -®&271Á;@¥Èªh[íò<Á²>dS“cˆŽ‹3ãÃŽØai@&%! -ÃrVü„…¹Žb·ïp(°USçÀØl”å¥Dº;X®V&-#Ð_HBϰX\Z^míF*íPh|Ödã(ÈØŒ¡/µ¥¹I¨,·®×QC@&R›)̪<ƒ"SòÊêÚº‡l~”UgÄ…øºX›ê¨®Ú¡»‰Žx…BR^mh¹Ó=0òz>LKLd\ þÞ¯+ -Ócƒ}qY*É" )ͦ¢†ž•æ—Qô‚eS8ÊêžÜMºàÀž¢¯¡(#Aô¥p„úD\r…ªŽ©µ‹oH\fIZNaê¯~p,Å‹\¡ˆñ.ˆØóN÷ãÉwŸ (c²§ÐÀìlª¼ŸsÊÛq‹1B¢/…"Ü–€ýúöü/&Ý-0™àp¹læpPþõK'½`YŠbY"á(“V -Cø 0öoqô>só~%zÖw -ÿs þË¡‡÷ÚQÖVÁ²D°¿ÊCK‡ßP”á„£l¬¿ãMEÁõ(@ÿM«”ˆ¾Žp™HÉ“uû‡'Ü)}º„;ÅfÀ7bIŽôصÅDKU°,çûÄÿµ0ûKü›ý2
‡ºÝãøé‘l3Ö±ïadß²ÄÊV3²FÙE‰Tö,i±>vɾ"TH˜“6Y’–#‘S$E$Œ-êyuî{hy®žCó\3ÓU×ù¼óÂÌÿÏ÷óýýnpµ‘Uß«,§êZgïЛÉiâäØË¾{ÍÕ91®xéÿÏKê°nü8ø%tÌ}O¤—5´=[ñ´¼§ƒp!;&ÐÃÊHÖbãÏXdÿ ¥V™ -ï›SÝ|¯ïåøÙpÿƒ[µÅ)>fz`ëG±23¬ÿù62ȲI`+“Õ0´òŠÏ»ßrlr®þ=íåÑ~{Ít$~åûåJª~[áÛ¼Q°‘‘Ž_NÓØÖ;,©¸î6ií'~*²s1î»
5äÄ@‘ŒîÓÃýè##‹/[™’žÅ>¿èØ×£ÓÓ“ãÃO»®WçÆ¸á±¿ò¼\‰d=Ýg~£àIƒDRQ×|¯_tfySG¸xI¯‡`#K‰<äh®§,%62F2Ú:ð7ùö€—]ÃhÛx…&\¾ù ÿÕøÔôÔÄÈóG-uÅ`^Ú|5/2‰Öž -ÐÄ -BÇ“¡±IÒ´ìp³¦(9ÜÇÁ\_UV\€›“
‰`!)@+ÈaÃ×Sà/ΘÉÉ'ŽÑ4²öIÈ¿tã~ÿð—y¹|¿Ô€ÕÉİ -}çD… ÁŒAǘ¬ìœ\Ü<<¼ -ÐÊ -¡2'.ØËÞÜ@CAFRTXP€ŸÀM+ÈÂ¶ŠŸ;Û`—ÓáèŒóà~98únš8çesun\ »•¡¦¼„ ©²ijÀ— ’BHGZ -)2˜1$;ŠWPTBj£,F€ÁÈÉÊl”–’B£Ñ’Ô––‘STÓ1Âí=–WÝÜÙ÷r|jf–89:ØÓÞt!;.ä€#ÎHW]##–”ˆÐÊ - -¤6AÁ!Ç#OÆ%gUÖßììy7³°¸ø~ž8ñúùã»7ê/e%ÇŒ<pìè?_š@...ÎNûVS -´¶0‡æ¦;
t5”1h~.6FŠT=#‚ƒO£ n<>Á§’Îæ—”•WT”Ÿ/+-).*,,(ȧ.…E%¥åU5
×[»zž
ƒS¿ôñã‡EPe#ƒ};n®\ºpþŸÅ…àIòrs²ÏeefÐÈ -gºMW}“´'V…B†ä@+ê˜Øí8™’[Z]{µ‰@ 456Ô_©««¡:µuõ
MÍ·Zï=êx569»°ôñ?>.½Ÿ=ø´»«ív3¡¡¾<ÉåK—.VWUV”ÓÊ -OÌ9_ÓtóNû]@G{[ë––ÛÔ§åNkÛÙ/都Ò4Œ÷ÐAzšˆÒ5¨¨("E†AFÄ‚k»°2²ÂŒe„eí aYEG‘QÀCd%´„%„„ôÐq¿ïÞ ¸ê!ç,yþŽææò{Ê›-.Ã×’š©„±¾>@E§RHµø²bl~^.x’¬¬×¯33ÒÓRÅè -òQƒA§w -ªn±~ -éK£¥¾,÷ÕƒÈÐÀý›ÝVb¬Ìu5T@˜Áe6jg~Д†&sæ¦ôؼÛ70äbäß^dæ—V“Z;X¼®^IMM
ö÷¾$°Iw®†Ý³ÉÍq‘ù=-f"t&ºÆdd†nJÒ”N‹¸ùàÉï9Øò2ÂXOŸ$Ȧ¦¾°;šëÊòRݼrúèžÍîNö¶¦†ºšªÓä„Àç0CªÄØðM¹ -mÊ3aÑ÷¿Ì|[‚¯oj£1&lJ -½/;©äªâœ—o\Øç³ÞÅccn¬¯>Zg¢U ¿†ŽÒ”k<¶|çrñúíøgio -ËkHÍí¶„±)-a”Ñ[ð…Y/Þû9ðÐ/·Uöó-ÍŒ¾‚)÷ùÎD«¬15-=pSb–9»{ƒ¦ü 6eRJvA)ØÔÖÁäð%ŒMi
"QÆcÑšë+°Y/ãoE„?²gëú5+Í›cb £¡ª:ó3a†2¦¨¬bÌÂvÉ*×
ÈM)lÊâÊ:r“Ít÷"ŒI ›ªB£ŒÛÙN”e'?޼|ìÐNï¯W/[hmn¬§>| -RÖßÛÓÅcw‚0«(ÊM{û:8 -)“—…ëd2ò ÈÌæ-sÝ´ïØ™ˆÛ“³±°)Û›RÒ”
k®P™H˜µ6ÖáKÞ êRHàá^®+í¬Í -´¥¬‚ж‘ÆÉs§pXÌã”7Eøú‘M)AL¢‘fŒÿ²_çßPÿ{ -R„©O™„¼†¡Õ.¿°KY…•z>0ÆÁôçýf˜²Ž¦êÒ?2.†Úkc¢¥(M$ O;d—o=ÓÑûydõ ÿƒX - ƒ8 -C6Œ!+!§œ;êaI³-`†-!¥•t6Ø{=—B.©m¡Ð†ÄA“×eKMInJt‡™¶¢4‘&2~¢´¢¶™GPtrnI
†ì ƒðÇD6@y_ó$'9:ÈC&%<™°”¢–év÷À¨äœ'5ï?
-¹¤¶…Bdî0d#ƒÝmõOóÓâŽ{ïØ¬«"#2?QZQÛÌÎ#(:%·¤CöAx›ø>Ρu·Ö•å]=æå¸i2I„0™°†Ì=(:9çIÍûO€Â¥µ¶”œ¼ßa£Ž‰Hà]¼h2-ÓíîQ€â8Ù0ÒR[BN9wÔÓ~ƒŽ’4‘°![0ÙJ„Ì-0*)çq5 ƒ8 -!û‚!«)ÉM‰ò°3ÓV”&ò³Efbë™tAÆD6@y_ó$'9:ÈC&%<†LÓÄÖ5 2!kžDÊ \±}b"‹ -tßnªÅÙ2IMWÿˆÄ;ůš»ú„¿iÈ’¢Ý²•ó [cl³Ï?üÙ ƒp÷YõE¶Cf´mï‘ð„ÛªÞ!dc_„³)dï&E¸Ùš°AÆ…I¬Xm´uÏá³Wo=|Ùô±!ûÈ |ÍA¦É™B¶Þz÷¡3W²¼hêì£2w“Èš²ÄÈ -rRãÂ<˜:…LÒÕV•ÌÍ—Íå@20&D²~©¸UX!ÈN‰
õwg2h²>J¦õG2{7ßHÆ%ûF$««(ÊNŽÙ¹aÅÂY4ŠáÉôMÌ&ËDÉš&ƒe -øÜhMÉf/Z¹q×Án<xÑÞE$‚d@#üd$#S+;gïàÈÄŒüÒšÆö.Ù€’?“5£d™Üè"™¹ºdTK['¯ ˆ„ô¼’ê†6‰LÉ -Áô‹KÍT -EbH°Œ$û¢Lv>)j‡ëUÉ´P2}2…Æ`ºû‡Æ¦d*„bi?$P²ï_Q²7Ïî];—t`ûz"Ù•É)´YWlØ“œ]TQG$ûÉ€fÃÃ?P²ž÷MOï^={lÿ¶u.ógLAÉtT%3± ’±c’³ŠÊëZĽàIÖ×ÓÑôäΕ3G÷m]»%3VŸÌa¹›ÃË*DÉ:!À¢LÖÝñúñí˧ìݲfé¼éfê“Y£d,É -™¤«
ÉÀßB$ë—Š[…‚ì”ØPw&ƒF!ë£dZ*’™ÉB¢¹™(Y3$xP²oD²ºŠ¢ìä˜V,œE£ªL¦g8IU2X4IÖ+n©+/ÊJŽaÉ,LÔ'›iïêu<óúýçÍ>A2€C™¬%+ÌâqØ~ËFO¶ÀÕgGTÒye²/`ø-ŸÇa¡dÖj“M ’¹¬ß~ éܵ{ÏÞ d_¿C2 ~2”lÊŒù.ë¶í?vöêݧMï{P²hò{2.‡å;j2c”lÙÚûŽž¹rçISGO$þ›ÌÍ~´dfÓç-]³eï‘Ó—o?~ÝÑ
É -øÜèÑ’ië’È:ƒé—š#¨ŠÄàÀM¦…’é“)4ÓÝ?46%[P!lKû!Ðìg²f”,“B$3W›ÌBû‡]:Šùà -’¹H²º_$Ù»'×ÏLX=?ÌŸIÖÉ€ƒá7IVú9ûñµÓ¶š7ÙP¯.$™K²±TŽd`6c²ÊÒ‚ìGWOíߺrî¤Q$™=[2Q ’/¦d%o^IMÚ;gâÈnNH41ɪ+K>½yqrßæ³'ŒÐÓÉÞÖÚª%’%Ædß¿~|}ÿò‰½›–Ï?¼ÎlÛ Pch`’翺wéøžËfŽÖÉ€.’¬¶º¢8ÿåÝ‹ÇvoX:cìP¯îÚÛ˜‘ìæ3$³0ɪ*Š>¼¸sáè®õK¦‡éÛÍÉ€&CC}mUyQÞóÛçì\·xZˆ_WG’E¦dú¼¬[é‡w¬]4uŒ¯§«P`Óºk²xyÊ9$31É~–ëßgiÓÕ‰kNíëé"´%ÉZ ÐA’Õ0ÉtZZ)‹‰ -öñpÚý=YW&Yt¼<™$ËA20‡1Y™>W—©Q)e&™³Ãß’YÛul*–+S²B’,M¥J"ƒ¼9’õ„GÇmO>{ãiΗoHÜ%KQHÅ$™;G²Ááâ™’ý@2àÄ/Y;&™ØüÕ Ï\òŽ$ûU‡dÀ¡q2¹TÁ–ÌŠ$ëÒkÿäy«¶8}íqöçR’ì7’»?“ŠX“Ù“d£&Í]¹uÿ©«²J+‘8ñMæä6päÄ9±[’R¯<|[P‚dÀG²–VÖ¶öN=Œ˜0{Åæ}'3¼ùTRYdÀ…W²6¶:÷è?|ü¬å›öž¸|ÿõǯߑ8ñOÖoظ™Ë6î9~éÞ«üb&Y’+~ÉlÚwêî5t쌥v»x÷e~qEu-’¾É»õ:}Éú]G/Üyñ¡¨¢ -É€ÏdG×>~!Ó¯ÛyäüíçyEåUµõHìø$kÕÚF tõô3uÑÚ‡Óoeå鑸ý›ìÙM®d-H²¶¡‹§ïè)×$ªÓµYïõå?‘¸ðLf'tñð ŽŠ‘)ÕŽIVƒdÀo2g&™D¦Ti2u¹ú2$Nÿ%;—"7+™wP¤DªP¥‘d…HÜø's'ÉÄRE -’™þI–C’%Ë㣙d]‘h24•¬£5{²±TŽd`&S²o_ržÞ8›¼=.:<@Ô›+™(É€c²¦d‡â„F2 ‹$«ûE’½{rýÌÁ„ÕóÃü™díè1~“d¥Ÿ³_;}`Ûªy“ýõêB’Y!ÐbLVYZýèê©ý[WÎ4Š$³G2 È”¬¤àíÃ+©I[bçL9ÐÍ É€&&Yueɧ72NîÛ¼bö„z:ÙÛZ[µD2 Ęìûׯï_>±wÓòYã‡÷ïѹƒm$j
L²âüW÷.ß³qÙÌqÃú!ÐE’ÕVW翼{ñØî
KgŒêÕ½S{$z˜dUE^ܹpt×ú%ÓC‡ôíæˆd@“¡¡¾¶ª¼(ïùíóGv®[<-į«£ -É€&ÙÏrýû,mº:qÍÂ)£}=]„‚¶$ÙÿC2h’¬†I¦ÓjÔJYLT°‡‹ÐÉ€"c²2}®.S£RÊ$L2g3“Ý|†d`S²B’,M¥J"ƒ¼‘(k”,E!“dîHt57Y¼<å’Y'“KÅHÔý™,PÄ•¬+“,:^žL’å pã™ÌÚ®cSɰX4#YoQ@xtÜöä³7žæ|ù†dÀ¥9É„/ˆK8dJöÉ€ßdí˜dþaóW'<sýÉ;’ìW’+~ɬH².½ùOž·jÛÓ×g.%É~#°áÌž$5iîÊûO]}”]PZ‰dÀ2'·#'Ήݒ”zåáÛ‚$.¼’µ´²¶µwê9`Ä„Ù+6ï;™ñàͧ’Êj$v<“µ±íйGÿáãg-ß´÷Äåû¯?~ýŽdÀ¡9Éú
7sÙÆ=Ç/Ý{•_Ì$k@2`Á7™MûNݽ†Ž±tÃîcï¾Ì/®¨®E2`Å?™c·¾CB§/Y¿ëè…;/>UT!°ãLàèÚÇ/dÚâu;œ¿ý<¯¨¼ª¶É€
¿dZÛ„®ž¾c¦.Z»ãpú¬<=’>ÉZdmBOßÑS®IT§k³ÞëË"°ãÌNèâá#Sª5Z“¬É€ÿdÎL2‰L©ÒdêrõeHš•Ì;(R"U¨ÒH²B$.ÍIæN’‰¥Š$³ü›ìÙM$if²±TŽd`žf%"˜ï¿dçRäñH€d`qÿ$Ë!É’åñÑL²®H4šJÖÑÎÉ€S²o_ržÞ8›¼=.:<@ÔÉ€*c²¦d‡â„F2 ‹$«ûE’½{rýÌÁ„ÕóÃü™díè1~“d¥Ÿ³_;}`Ûªy“ýõêB’Y!ÐbLVYZýèê©ý[WÎ4Š$³G2 È”¬¤àíÃ+©I[bçL9ÐÍ É€&&Yueɧ72NîÛ¼bö„z:ÙÛZ[µD2 Ęìûׯï_>±wÓòYã‡÷ïѹƒm$j
L²âüW÷.ß³qÙÌqÃú!ÐE’ÕVW翼{ñØî
KgŒêÕ½S{$z˜dUE^ܹpt×ú%ÓC‡ôíæˆd@“¡¡¾¶ª¼(ïùíóGv®[<-į«£ -É€&ÙÏrýû,mº:qÍÂ)£}=]„‚¶$YÇšƒ$«a’é´µRìãá"´C2 Ș¬LŸ«ËÔ¨”2 “ÌÙÉ€&S²B’,M¥J"ƒ¼‘(k”,E!“dîHt!X\ãdr©8É€º?“Š(C2°8$‹C2°8$‹C2°8$‹C2°8$û»ôþSóãp|DJ‡8Ê܉\æRîÌ5wcîå:ÆÜ -c“ÊeŒ¹¥0ÆÜ‹aîr¿—{.‰pJQ¢}ßïú~÷5«÷ûœ³^û|~x>þƒçö„8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8&ƒ8Û&kÚÉ`1['ëïÂd°“AœM“ÕV&ó g2X¶ɚuà©Lfb2è°a2£›:ÙøÀЈÈè˜W¦4&ƒ6ë'3Ý<¼:
œ0oUDTt¬:Y6“A‹U“Q&s0Ý=¼:š8õŽ¨Ë±ñ¦´L&ƒ&+'³+î`pq¯Ó¼ËàIÖì<våA|“A‡õ“•r©\·E×!“®ÝuüêÃ×Ié™Ù¿™l˜¬\•z-»
²hÝî×%$§g14Y=™céòUë·ê>lêâõ{N^œü%ë'“A‹M“5hÝcø´%öžºñäÍGu23“¡`ÖNfïèìZa›ž#¦/ݸïôͧoS2˜Ú¬›¬¨}Ig×êÚö9cÙ¦ýgn=KLÉøÎdÐdÃdj4n×{ÔÌ囜½—˜ÊdÐaýde”ɼûŒžµbËÁswâÞ¥~e2h³r²bÊdk6ñî;fvÐÖCçï>ÿI™,‡É ÁêÉœÔÉ||ÇÎ ÚvøÂ½Êd?~1´Ø4™§ï¸9+Ãò&ûÆdÐfËdµ<Û÷778ìÈÅû/>|f2è°v²Neó›ìŸÎÀ¿Ùß“uhª;Y%u2¿¹ÁÛ•É^2tý9YH€_&C¡³y²€àíG™–øc²ð -Wîd)oŸÞ<½oãÒé#z¶iXÍÕÙÑ^k²ÚÊdþ!áL‹¨“}ÏHI|vëÌþMËfŒìÕ¶QuWç’övE5&kÖq€`hx¤2™‰É 'o²ÔĸÛgl^>sTïvkTМÌè¦N6>04"2:æ•)É #w²¯©ïâîœ;¸eŬÑ}¼•ÉÊhLf0ºyxu8aÞªˆ¨èXu²l&ƒ&³9G™ìÓûçwÏÚ4{L_ï&5+*“Ëg²"Êd£»‡WçAç¯Þu96Þ”–ÉdЦLöë‡2Ù‹{oš3Ö×GÌ©€ÉìŠ;\Üë4ï2xÒ‚5;]yŸÄdГ;Ù·¼ÉÂVÎçëã©=Y)—Êu[t2yáÚ]ǯ>|”ž™ý›É %o²Ï^Ü¿x$,xî¸~í=kiOV®J½–݆NY´n÷‰k’Ó³˜:ò›¬¬S‰‚&s,]¾jýV݇M]¼~ÏÉë’¿dýd2hûïd/•ɶÏõS'«¤3YƒÖ=†O[²aï©OÞ|T'33´X7™½£³kµ†mzŽ˜¾tã¾Ó7Ÿ¾MÉ`2èùÿdG·hOVÔkõFm{œ±lÓþ3·ž%¦d|g2è°z² -5·ë=jæòÍÎÞŽKLe2è²v²2ÊdÞ}FÏZ±åà¹;qïR¿2ôüo²˜KLVL™¬bÍ&Þ}ÇÌÚzèüÝçï?)“å04Y9™“:™ïØ9AÛ_¸÷B™ìÇ/&ƒ6&óôñ7geXÞdߘz¬Ÿ¬–gû~ãæ‡¹xÿŇÏL]ÖMV©l¥|&û§#ðïfÛd~sƒ·+“½d2XÀ†Éš2¬ò÷dšêOÖ¡¿_@Èö£—˜ùs² -žÌÞÑÙµZÃ6=GL_ºqßé›Oߦd0tåN–‘òöéÍÓû6.>¢g›†Õ\í󙬨}Ig×êÚö9cÙ¦ýgn=KLÉøÎdУNö=#%ñÙ3û7-›1²WÛFÕ]KÚÛͲ -5·ë=jæòÍÎÞŽKLe2èË›,51îöÙ›—ÏÕ»]ã -ž¬Œ2™wŸÑ³Vl9xîNܻԯL]¹“}M}wçÜÁ-+fîãLV&ÿÉŠ)“U¬Ùħï˜ÙA[¿ûüý'e²&ƒ6³9G™ìÓûçwÏÚ4{L_ï&5+*“Ëw2'e2Oß±ÿa¿þé¾â8ާs¿_×u/®Ë¸s/eÜ^WuTô^Ú©ãÒLÓÒpÝ®3å^ISieZ_–¥]Ûà®é–ÆX¹ºX›nÆ%[LŒkë–Rt³öv1ELÅ—Òdç|¨¤s?÷£?.y?þ†gÞçuôõ&³eE¶´‘ -(²•%™ÅÜR_¡ÏÒ¨qd\²Èb_e”F§¯4nDÏ% €žK"²Q‹ÙT_©×iÔJòȘ\Š,Yg¨2¶v‘¡M†NdH¡<ž®Ï%ŽÌˆ#‹RúÚ,$*ùèó‘áÊ -bj¹ÖxõrÍù3'ò2Ą˥b#Ófdt6Oè!Û±ûuMú;ÇO–}ðÑÇ5uõÆO°Ïh¬«¾t®âtq^Vjœj§¿7ÚýLô¹´ËÑÙ]²'6%ýX^щSïŸ-¯¬€JeEyYiIqANfjBt˜\†^K6ƒ¶i’¡åï@crœ\=¥A¡ªØ¤ƒoçä½WX¤7Å -?o‘3—ec÷¯2–£@äå¹/NóÖ¡ôm–Nw -º,mæ‘é) 1ª0…¿D,ćló$[{/Ñ)º{û*Ã"£cÞˆ3I£I€’&)1!nÿ^UøN¹ŸÄÃ…¿vÈlDFœ2.ªìeŸíAÁ¡a‘{Ôê( -0 -%!PS-Adobe-3.0
%%Creator: Adobe Illustrator(R) 15.0
%%AI8_CreatorVersion: 15.0.2
%%For: (Dustin Rinehart) ()
%%Title: (Untitled-2)
%%CreationDate: 6/25/12 6:52 PM
%%Canvassize: 16383
%%BoundingBox: 94 -593 707 -5
%%HiResBoundingBox: 94 -593 707 -5
%%DocumentProcessColors: Cyan Magenta Yellow Black
%AI5_FileFormat 11.0
%AI12_BuildNumber: 399
%AI3_ColorUsage: Color
%AI7_ImageSettings: 0
%%RGBProcessColor: 0 0 0 ([Registration])
%AI3_Cropmarks: 0 -600 800 0
%AI3_TemplateBox: 400.5 -300.5 400.5 -300.5
%AI3_TileBox: 22 -588 756 -12
%AI3_DocumentPreview: None
%AI5_ArtSize: 14400 14400
%AI5_RulerUnits: 0
%AI9_ColorModel: 1
%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0
%AI5_TargetResolution: 800
%AI5_NumLayers: 1
%AI9_OpenToView: -133 17 1.69 1805 1060 18 0 0 -1873 98 0 0 0 1 1 0 1 1 0 1
%AI5_OpenViewLayers: 7
%%PageOrigin:0 -600
%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9
%AI9_Flatten: 1
%AI12_CMSettings: 00.MS
%%EndComments
endstream
endobj
76 0 obj
<</Length 9743>>stream
-%%BoundingBox: 94 -593 707 -5
%%HiResBoundingBox: 94 -593 707 -5
%AI7_Thumbnail: 128 124 8
%%BeginData: 9614 Hex Bytes
%0000330000660000990000CC0033000033330033660033990033CC0033FF
%0066000066330066660066990066CC0066FF009900009933009966009999
%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66
%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333
%3333663333993333CC3333FF3366003366333366663366993366CC3366FF
%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99
%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033
%6600666600996600CC6600FF6633006633336633666633996633CC6633FF
%6666006666336666666666996666CC6666FF669900669933669966669999
%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33
%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF
%9933009933339933669933999933CC9933FF996600996633996666996699
%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33
%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF
%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399
%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933
%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF
%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC
%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699
%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33
%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100
%000011111111220000002200000022222222440000004400000044444444
%550000005500000055555555770000007700000077777777880000008800
%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB
%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF
%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF
%524C45FDBCFFA8FFFFFFA8FD78FFA8FD0BFFA8FD6EFFA8FFA8FD0FFFA8FF
%A8FFA8FDE7FFA8FD19FFA8FDE1FFA8FFA8FD07FFA8FFFD04A87DA87DFD04
%A8FD0BFFA8FD66FFFD05A8FFA8FFA8FFFD07A8FD64FFA8FD07FFFD05A8FD
%0BFFA8FFFD05A8FD07FFA8FD60FFA8A8A8FD13FFFD04A8FD5DFFA8FFFFFF
%A8FFA8A87DA8A8FFA8FD13FFA8FFA8A8A8FFFFFFA8FFA8FD54FFA8FD06FF
%A8A8FD1BFFA8A8A8FD05FFA8FD52FFA8FFA8FFFFFFA8A8A8FFCBFD1BFFA8
%A8A8FFA8FFFFFFA8FD50FFA8FD05FFA8A8A8FD21FFA8FD05FFA8FD4EFFA8
%FFA8FFA8FFA87DA8FFCBFD1FFFA8A8A8FFA8FFA8A8A8FD4EFFA9FFFFFFA8
%A8A8FD24FFA8A8FFFFFFA8FD4EFFA8FFA8FFA87D7DFD25FFA8A8A8FFA8FF
%A8FD4CFFA8FFAFFFFFA87DFD27FFA8A8A8FFFFFFA8FD4AFFA8A8A8FFA8A8
%52A8FD28FF7DA8A8FFA8A8A8FD4AFFA8FFA8FF7D7DA8FD28FFA87DFFA8FF
%A8FD4AFFA8A9A8FFA87D53FD29FFA87DA8FFA8A8A8FD38FFCAFFFFFFCAFF
%FFFFCAFFFFFFCAFFFFFFA8FFA8FFA8A87D7EA8FD29FF77A8A8FFA8A8A8FD
%2CFFA8FFFFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF
%FD05A8A9A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF
%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFD07A8FFA8FFA8FFA8FFA8FFA8FF
%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FD12FF7DA87DA87DA87DA8
%7DA87DA87DA87DA87DA87DA87DA87DA87DA8A8FFA8A87DA87DA87DA87DA8
%7DA87DA87DA87DA87DA87DA87EA87DA87EA87DA87EA87DA87EA87DA87EA8
%7DA87EA87DFD04A8FFA8A87DA87EA87DA87EA87DA87EA87DA87EA87DA87E
%A87DA8A8A884A8A8FFA8FD10FFA8FFA87D7DA87D7D7DA87D7D7DA87D7D7D
%A87D7D7DA87D7D7DA87D7DFD04A8FD057DA87D7D7DA87D7D7DA87D7D7DA8
%7D7D7DA87D7D7DA87D7D7DA87D7D7DA87D7D7DA87D7D7DA8FD057DA8A8A9
%A87D7DA87D7D7DA87D7D7DA87D7D7DA87D7D7DA87D7D7D847D7D7DFFFFFF
%A8FD12FF7D7D7DA87D847DA87D847DA87D847DA87D847DA87D847DA87DA8
%A8FFA87D7DA87D847DA87D847DA87D847DA87D847DA87D847DA87D847DA8
%7D847DA87D847DA87D847DA87D847DA87D847D7DA8A9A8A87D7D7DA87D84
%7DA87D847DA87D847DA87D847DA87D847DA87DA8FFFFA8FD0EFFA8FFFFFF
%A85352FD177DFD04A852FD2D7D52A8A8A87EFD1A7DA8FFFFA8FFA8FD0EFF
%7D7D5252FD177DA8A8FFFD317DA9A8A8FD1A7D527DA8FD0AFFA8FFFFFFA8
%7DFD0452F852527D527D527D527D527D527D527D527D527D527D527DA8A8
%A87D5259527D527D527D527D527D527D527D527D527D527D527D527D527D
%527D527D527D527D527D527D527D527D527D527DA8A87D7D527D527D527D
%527D527D527D527D527D527D527D527D527D525352537DFFA8FFA8FD08FF
%A87D527D597D5252527D597D7D7D597D7D7D597D7D7D597D7D7D59FD047D
%A8A8A8FD047D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D597D7D7D
%597D7D7D597D7D7D597D7D7D597D7D7D59FD047DA8A8A8537D7D7D597D7D
%7D597D7D7D597D7D7D597D7D7D59FD057D597D527D53A8FD07FFA8FFFFFF
%A8FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFA8
%7DA87DA8A8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FF
%FFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFA8A87DA87DA8A8FFFFFF
%A8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFA8FFA8FD
%26FFFD05A8FD2FFFFD05A8FD21FFA8FD04FFA8FD20FFA87DA87DA8A8FD2D
%FFA8A87DA87DA8FD20FFA8FD26FFA8A87DA8A8FD2FFFA8A87DA8A8FD21FF
%A8FD04FFA8FD1FFFCAA97D847DA8A8FD2DFFA8A87D847DFD21FFA8A8A8FD
%04FFA8FD1FFFA8A87DA8A8FD2FFFA8A87DA8FD22FFA8FD04FFA8FD21FF7D
%7D7DA8A8FD2DFFA8FD047DA8FD20FFA8A8FD05FFA8FD1FFFA87D7DA852A8
%FD2DFF7D52A87D7D27FD21FFA8FD04FFA8FD20FF5252A87D5352FD2DFF28
%7D7D847D277DFD1FFFA8FD06FFA8FD1FFF52527D7D5252FD2CFFA852527D
%7D7D277EFD20FFA8FD04FFA8FD20FF7D2752272752FD2DFFFD0652A8FD1F
%FFA8FD06FFCBFD20FFA8275252FD2FFF7D7D527D7EFD21FFA8FD04FFA8FD
%23FFA8FD31FFA8A8A8FD21FFA8FFA8FD04FFA8FD79FFA8FD04FFA8FD79FF
%A8FD06FFA8FD7EFFA8A8A8FD77FFA8FD06FFA8FD7FFFCBA8FD77FFA8FD06
%FFA8FD79FFA8FD04FFA8FD79FFA8FD06FFA8FD26FFAF84FD56FFA8FD26FF
%840D363B84A8FD4DFFA8FD06FFA8FD25FF2F300D603B6084FD54FFA8FD23
%FFA8360D2F0D363B3B3BAEFD4AFFA8FD06FFA8FD25FF2F362F362F605F60
%5F8AAEFD50FFA8FD23FFA8360D360D360D5A5F605F5F5FAFFD46FFA8FD06
%FFA8FD25FF2F3635362F362F605F8A5F5F5F8AAFFD4AFFA8FFA8FD23FFA8
%360D360D360D360DFD075F84FD43FFA8FD06FFA8FD25FF35363536353635
%3635605F8A5F8A5F8A5FAFFD49FFA8FD23FFA8360D3635360D3635360D60
%5F835F8A5F835F8AA8FD3FFFA8FD06FFA8FD25FF35363536353635363536
%3560838A5F8A838A5F8A83FD3DFFA8FD08FFA8FD23FFA8360D360D363536
%133635360D605F835F895F835F895F8AA8FD3AFFA8A8FD06FFA8FD25FF36
%363536363635363636353C3660838A838A838A838A838AAEFD39FFA8FD08
%FFA8FD23FFA8363536353635363536353635363560838983898389838983
%8983AEAEFD36FFA8A8FD2CFF35363636353C3636353C3636353C3660838A
%838A83AE838A83AE83AEAEFD35FFA8FD06FFCAFFA8FD23FFA83613363536
%35363536353635363536356083898389838983898389838983FD35FFA8FD
%2CFF353C363C3560363C3660363C3660363C368A89AE83AE89AE83AE89AE
%89AE89AEAEFD31FFA8FD08FFA8FD23FFA93635363536353C3536353C3536
%353C353C3584838983AD83AD83AD83AD83AD83ADA7FD31FFA8FD06FFA8FD
%25FF363C353C363C3560363C3560363C3560363C368AADAD83AE89AD89AE
%ADAD89AEADADADCFFD2EFFA8FD08FFA8FD23FFA83C3536353C3536353C35
%3C353C353C353C353C358483AD83AD83AD83AD83AD83AD83AD89C9C3FD2C
%FFA8A8FD2CFF363C366036603660366036603C6036603C603C603C8AADAE
%ADAEADAEADAEADAEADAEADC9C2C2C2CFFD2AFFA8FD08FFA8FD23FFA83C35
%3C353C353C353C353C353C363C3560363C35603684FD04ADA7FD06ADC9BC
%C3C2C2C2C9CAFD28FFA8A8FD2CFF3660363C36603C6036603C6036603C60
%3C603C603C603C8AADAEADADADAEADADADC9C2C3C2C3C2C3C2C9C9FD27FF
%A8FD08FFA8FD23FFA83C353C353C353C353C353C353C3560353C35603B3C
%35603C8AFD04ADA7ADADC9BCFD0AC2C9CAFD24FFA8A8FD2CFF3C603C603C
%603C603C603C603C603C603C603C603C603C603C8AADCEADCFADC9C2C3C2
%C3C2C9C2C9C2C9C2C9C2C9C9FD23FFA8FD08FFA8FD23FFAF3C3560363C35
%603C3C35603C3C3B603C603B603C603B603C603C8AADADADC9C2C2C2C3C2
%C2C2C9C2C8C2C9C2C8C2C8C8CFFD22FFA8FD2CFF3C603C603C603C603C60
%3C603C603C603C603C663C603C663C603C8AADC9C2C3C2C3C2C9C2C9C2C9
%C2C9C2C9C8C9C8C9C8CFFD20FFA8FD08FFA8FD23FFA860353C35603B3C35
%603C3C3B603C603B603C603B603C603B603C3C3CA7FD07C2C8C2C8C2C8C2
%C8C2C8C2C8C2C8C9FD20FFA8A8FD2CFF3C603C603C603C663C603C663C66
%3C663C663C6660663C6660663CA194C2C2C9C2C9C2C9C2C9C2C9C8C9C8C9
%C8C9C8C9C8CFFD20FFA8FD08FFA8FD23FFA8603C603B603C603B603C603C
%603C603C663C603C663C603C663C7D93B693C2C2C9C2C8C2C9C2C8C2C9C2
%C8C2C9C8C8C8CEFD21FFA8A8FD2CFF3C603C603C663C603C663C663C663C
%663C6660663C6660663CA194B693BC93C2C2C9C2C9C2C9C8C9C8C9C8C9C8
%C8C9CFFD22FFA8FD08FFA8FD23FFA8603B603C603B603C603B603C603B66
%3C603C663C603C663C7D8D94939493B693C2C2C8C2C8C2C8C2C8C2C8C8C9
%C9FD24FFA8A8FD2CFF3C6660663C6660663C6660663C6660666066606660
%67667D94BC94BC94BC94BC93C2C8C9C8C9C8C9C8C8C8CFFD26FFA8FD08FF
%A8FD23FFAF603C663C603C663C603C663C663C663C663C663C663C7DFD06
%9493B693B593C2C8C8C2C8C8C8C9FD28FFA8A8FD2CFF60663C6660663C66
%60663C6660663C6666666066667D949B949494BC94BC93BC93B693C2C8C8
%C8CECFFD29FFA8FD08FFA8FD23FFA8663C603C663C663C663C663C663C66
%3C663C66427DFD06949394939493B693B58CC2C9CFFD2BFFA8A8FD2CFF60
%666066606660676666606766666667666666A2949594BD949A94BC94BC93
%BC93B693BCCAFD2DFFA8FD08FFA8FD23FFAE663C663C663C663C663C663C
%6660663C66427D8E95FD069493B6939493B593C3FD2FFFA8A8FD2CFF6066
%66666066666660FD08667E9495949B949494BC949A94BC93BCA1FD31FFA8
%FD08FFA8FD23FFAE663C663C663C663C663C6660663C66427D6A95709470
%FD079493C3CAFD32FFA8A8FD2CFF6667666666676666666766666667667E
%949B959B949B949B94BD94949BFD35FFA8FD08FFA8FD23FFAF663C666666
%3CFD0766427E9495709594959495949493BDA8FD36FFA8A8FD2CFF666666
%676666666766666667667E71959595949B9495949494CAFD38FFA8FD08FF
%A8FD23FFAE6642663C66666642666666427E6B957095709570957094A1FD
%3AFFA8A8FD2CFF66666667666766676667667E9595719B959B949594C4CB
%FD3BFFA8FD08FFA8FD23FFAEFD0A667E6B957195709570959BFD3EFFA8A8
%FD2CFF6667666666676666667E7195719B7195709CCAFD3FFFA8FD08FFA8
%FD23FFAF66426666664266667E6B957171709571CAFD41FFA8A8FD2CFF66
%6766676667667E719C7195719CA2FD43FFA8FD08FFA8FD23FFAF66426666
%66427E6BFD0471A2CAFD44FFA8A8FD06FFA8FD25FF66666667667E6B9671
%729CFD47FFA8FD08FFA8FD23FFAE666666427E6B716B9CA8FD48FFA8A8FD
%06FFA8FD25FF6666667E6C7278CBFD4AFFA8FD08FFA8FD24FF8B427E6B78
%A2FD4CFFA8A8FD2CFFAFA99CA3FD4EFFA8FD06FFCAFFA8FD25FFCBFD51FF
%A8FD7EFFA8FD08FFA8FD77FFA8FD06FFA8FD77FFA8FD08FFA8FD76FFA8A8
%FD06FFA8FD77FFA8FD08FFA8FD77FFA8FD06FFA8FD77FFA8FD06FFA8FFA8
%FD77FFA8FD06FFA8FD77FFA8FD06FFA8FD79FFA8FD06FFA8FD77FFA8FD08
%FFFD79A8FD08FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8
%FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8
%FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8
%FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8
%FFA8FFA8FFA8FDFCFFFD87FFFF
%%EndData
endstream
endobj
77 0 obj
<</Length 65536>>stream
-%AI12_CompressedDataxœì½i“ÛF²(úê?ð}p„}cšFa‡=ñ".¾¾W^Âòœã'n(¨nJê3ݤn/c{~ýËÌʬ ->ûõúáfŸþcý€¯®.â¯ä™ÐÉlù -&x»|ik1ù^ǯêÇ뛫o_¯`êIUáåäuù{èºÅ×x¹xõý-\y¹z€åyÄõüå»Ú\¤öåý²z{MË«ó¾ânï6ïo—wÿÄ{GyJøÌ‡¿®nßßÀ2ÒœÓ(g£‹„þóßðwa.ô½8†%)ËQ‘壛OÝú¬þu½úý›Ñ›õÊ,Âäîá¥ÙŒz5Í'¿<Þ¬îþ±¾~0›|_™UøasµºïÛû7Kš<5íþš/üº¼{»z€-ÜÜ<>t•òXåË?W¸UÚ<à§÷«õ¯›ÿ 1^è$éb¤Çy5Òe”t”C×%=áB—E2ªJûXíþšÞ±/ìIQÀæüÛõÓÝõÛëõ7f½Í6~ww}åv±ˆG¥ùCÓ—Þo%¿f¼0õ‡‡ÕšÇÐ3ýÁƒ†hüÃKxè|}5ÝÜâúß#°¬Bn6oÍgö5}·?¾7 ÷¯`«~¾»^cŸêGú¤|õóÍ#|ôÝÝæñý÷ë7õ¥9à¿Þ-/ák£Ÿ^ÿ÷ê)_p¯^>^?¬ÆËë÷_íì¦u·™áfz+ÿï¿{¶z§ÊÝn®Î×ÿZÝlÞ{ÝÚ+ËõÕè?—wï÷wýóÍr½¼ÑuÛó‹ëÁ'KX#×·»v@§ -6f -éX':S:™èZà -ªD±Ž“8‹ -+Ã# Ys wS | ‚%Pö0…UŒaôÎ9Ð)Ò ÕJm-€Æf°E ¬†Ñ, -H~+ðM-ÔGÞÖ+対
1 =UÙ07Ú!"$±ÄÊÄ´¬Ød»ÜæÔfÜäÇ0RnÀ)ÛFŒSI¤¯ 1-ã–r#ŠBÄ2¦Ý2-2Íð6жoN[h?²–Ÿ ·Š[Éà–sËLSĦ´ÿ¦ÅÜ47î…msn3n2_z®¢ÿä§²äVØ–Û–qK½–HS>ñ°Céè‹Wõ°ÝÑ .ÔÛDeE/"U•ÆQ”f¨Î‰bØsÔçÄU¤«^äx!æ]òQ/a—/Õ¯ÎÙe}#L3äßHÇH$Ã/ã˲„.P÷4.» ß™æcdðP7ž§v -šüj
û)
{I^EÚ€)jô‚ô1ln -k“V ‚GÕ -K‘ó+2b‰«â,E¾0Í¢ªÜÂ`>¹ä'ñy¨ -Z§Äš¶ô³_3K\åß‹]¡š°(lª -ç¬_j© -é‡"Ö=EUx¢0T†JB§"Œ!ë•§å ‘ØD1˜[¥ 浜±.ÐX‡r«×¶ 9Ëp+Å+OL’œ±ïu‡Œ:)¥S1è -zFªg<†%2ú9ЄÌIv3’í&F¶cCMBH ò‚Œ›¨Iž‘6yÊvÏ i•+Ò,—¤].°C)?¶R><QyB¾óEПx¾÷s2ב?!å´‘ú5jÅ•‹ÿ3VÖ¤˜½5©PPÐé2¦b§y+ë[U l
•¬¡nÕiUCmªÕ¢í·²ÚS§7©Ó•:©(GZÔ©CI
ª¬TTŸNåé¢àD•&îd =:ù±Ù’--m6ÅœAwË;[·¤ÁªZeu¶%3Ýû§n7âtµYg›w´…ß”¼%u£éÎw6R‹(«O¬ÃD»e®…BØ)Fö:K -Ôz‚—ƉqœÆÓÈfÀßGÛ”Ð'ôAZgäOsú:°ó J¤ÆÉÑܧ©Oiœéq -8È×:?µƒ“¤„N¥óYtÎF%jT Žw¾I–ÉÍås2ž/È#}2Pflì%©cP!3%…ÌŒ2óL(…äF)£H+3!YfFŽ*"Ë¿œý&N7c5oF–1ÒLNˆ"a†´4¤²Ù«£ ¯Ž)É-FfA‰5W(«1%&ÏŽŒ¸)ä»jĶДeš\<DS£GEˆÕcV41‚‰Ñˆ9ÑÄN”‘OŒílî5qñ‘F¼&·$hiG#Cžq -ò[ÞÑ -nÝWÿsÕq¹`Ò&ä}Õo•×འ->ôG"ôQhdìôâ¤_ð2Í<⿪A!
:™[:™ZzËBií“d£T†nÒþÖå ϧ´V易¨†§Ö¤Œý!щ̀ F@=§€ÌKÅjøˆðòP>ªß`]Põ^ÀdÀ-` -ÑâÿpN±’tI”g%Ùe'é´’44ˆd&QÙIBK‰g'1šCßP¢Z–’¦pʼËVâ¬%l,!Õ ò¬%M{IêÙKœÅ¤Ëf"Ž¿%ú(‘ïoÈ'ç°KeFŽY3k›¥ZÈÚ"óS/ãm,òÓng:n zIŒ«$ªŒUà1Ô‡u\¥:x1Ž5º}ø:ô§upwœTUŒ—}¹áƒ%ʃϜv¥$žO4,FÇ¢Éa-tÇš(¶¬‰KYæ+]Œ®lj¼³HùR±^ÌhÃŒ>,1Š0•»…çèô^5˪¥mNç—Íþ(ûÏ¿§)jêŠ}
|ÁC¯é€6½ÊHq¤è4Gx®ÉÞï†;a5]E8À9QGÊ”pb -vi?JE -»)Ë‘ÆZQZgÊÂjÛ4fEQöilC÷9Ñ|Â_»Uf›ðoÅ3.íœñרçyÒŠf5%7áyOxÞaÁ4p y³³^„>¤"ÐTÖ‰ÔÈ:Y0ï¤cÖÔ3 ÿkܬ¾mÊŽž%)ÜRÏi:b_Îûpúþ›ä½™[§i S‹;Û¯àÇànk|›Îøù¶MÞÆ¥¨†8èyKn…Îf%V«(°\%ŠuUN%(œZú$§;Ú¢.RöÍ»/Ân*›µçŽSW–غ¹gLq‹`æÓŽ™{ó–I«ŽYûsnÎv›*¬ -:tÊŽ¥7–‘t3’nFÒÍHºI7#îf„ʲ}Ú¹z$p:7ÐìlÃsžat<×s¯ÝqÌ÷«
eëz{?úë·ëÕÃà - ¸ë*úB`X8‡´H
ø"EE‘Bã~®S¶!þŸ;C`œQ,à÷wq¬7èuFÁýšéŸ".j¨ˆ¡!â9op&PE“Ýæ=º!44âh¤˜›ñ_JØ“IÒdŸ¦Êåð¾$f“غ?Å™Ò8 - ö¼6'*’ÿŽ•‹È¦;ª¿bGcfÇ#?3îpÄÑCÓñ©é´äp>b€ -ã´œ1û
7Ïs°¦(òV°Þƒ±%^â·`}mä®ñ!ÌT#v·¶±žÆ‹AüŒ'ƒø2„QŸA\”²n
ƱA\<çê#¹›ZD;3°MªaŠè2A‰Ë›#+L+LW¹€)6¿¸PQÑØ`)ÑŸ˜Ý4û¨íæ§…àªÃ¼K -Á¥P{|KŽÀU]~%†ßvê¢ÔUTwèíöÀ[’”—:®îºõCnkÖ²xê‘¿+?uI¾¤™¼¤éºº™Ù,%ʦ'q–ñ:ÓÄ} -§eÏB׳’£^·3ø‰Æ”À~BG äH?iNxjø~¥òÂbºcœwFÑÓŒŽÉU+@¦"ã4õb¯`m½1ǧDéÍO$Í8³Áœm/S6àUìc]p~³œMõØbÑÄãAÉÆa[h˜qäž°}Bòž¹˜èÿÜjêaÞ* IÍ8ŒÇÅH"áúáK¨†ûÿ1ºã/
€|J#ã7¯½äƒÆû}Bë5{ -êS§;Ö…¨OàV׉úÔžäG£¾Ýzø¨ÏHfž1;0aÁ'è’Üì6‰“ÉD1Q^ä®ËÝ9#x áF^o;„W‚xsűSÛH8Ñ'ëÇ%Û§Éø9eÛÉŒ£²æÆÙÜf -pWG.¾½¶ºi–,4±JÚðøÚãñ2Z…9$Y9}¤¨¡+cSŠ}Ÿs]ÝRBWÞ³¿°Ñðq¯ËTç( úL›²_¼áôÅ`š*O-ñ -Pƒˆ@*W$Š -W”T¶bF.ö<[ª-zêÖnn³!uÅæ©D{›m›¬ÓhfjªÃƶÍ&Wmi©Oyo¶Y ÛöÄ)çÚèhj«Ír›zfÛBójYQ·Y]=ˬ(ë\öû‰µHÀ¹lÎR˜Sö‡ªþ”ŒU±j)ù}SÁ¯t*ø%sZŽY1ųBD5QòO9=YÕÀ´9ò[Ê~’ÝTÃÃ"i§Ég7©ü[Å¿”1W}ÅBˆu¹ï`j;H]©éàÂ4ÂÀº l톦¿¾sˆmcèž¶³^쯩°· ‚ꨨ°µžÂ!¢¼:,AÚᢼê–åŸ.Ê·c4ÚvŸ£Dyµ/5Z—(ï•ó¹2âE”ÀdÆÛSÑö ê
2"·29ãSRâ™|âÆ”‡ ®É£fÑ’SÚç&•ØÀê9¹IÙÀjë&%ÙcÉ6n3i_üÀêfhµ\òùÞUýešMÔÞò/rê›çÞT})š'_ÙR/sÎ4!°ü8]³ùfë9'žÙ÷Èf:Ÿ4”7+o¬ÕEøë’ÈwHÀ3+›æÖ¸Q9¯é\#AGÜ’ Ñ7©ò<U]•²„PFÊgé9rÀG#èƒ0œjx©ºÈ‡ÜSÃWÿ¨?7™`$¡ÏLqr˜Y«5åz?E ´Ž;Uãc×¶édºt.žZEy”¦J$(1ÐvY$‡E¤ªž9Ýœ¼»/Ö6éœËç"îŒèÝ–ŽÜXØSNR"¾Ÿ1)è5y:2Eæß9¡X÷;cQ -ž’·ÜÔþ‰ÔüV„‘'$)”Öbo~sKÚͯñ.M 1ár5es¾Pü‰&Æõ´&v¶$\Q’“YGTvE-"Ek=#×J“¨´âÔ¤9ù樂Ôd]@|Ps?¢€”’ˆs껢³ž“__ÊŽ”:ÞÆ‰²$‚Ç_Ü…Zrª ÀqÙ¥i,HaNTrÔVq‘›Ì¢B3§Ö‹1#Z‰ÈxF¾ŒÚð”oÔsÈhm¥š®]¡¤˜¡Õ©õlÌ$Ï;ëV„SJÜ ©UÅÁ1·Ñˆ€çT«Œ•xcô&Œ®j“º™úm¥õm`ꦂ,.xÂ…Q¥M?µ¤Aëšx§'gøWçqzr†u§'gøWçqzr†u§'çó¤uzwÄ.Ñj§$Õ-59Ùi«ŒÄ¹ý–-ZaêªIÚå²Ñª …)Q›‰;§®)öªtmÞh‚¼!s‡“£»Ÿ$haꑬÑò ®):.a«‚Æ_qAœÛ]aU–Zµ°Ž¥•]Ÿ©u/Û‰ÂÊND-ÓqwSq8åäû¦P¥-&“X×ÓLyù+ÅÕU’ÙREÆf´Ìl1qIW”‘š2~U™®º2bü?UñT9‡?9IΉÅEa»ÖŒx®–VmX[ÿÕ™Urµ£8´"ª œÔ¹¢"»·²zÜd§ -h‹âÇÓö´Ü²}¯m_³[£ÓB.žæ¦CC>?Ua³”$ Ìm -ä }ÔÚ ´;Ìþª 1Zg¤Vv𸺃a; +v|7ëƒÓ«íLœb²íó¡fÃtÁ%Ð]ªìĤ©:Ú¢çl‰N„'dE2ög1;³½™Á"c“ÒÄ -SxÖ™°r’_;) òï/æÊ -Ê“h=©–½ˆç¶Ìzö%dØFd2%ß¾Úzó•d&…Yò?!Ü‘H#éÒ)ÙÊ‚]±L‰‘œŒ¹õè3õE'äkSŸ>¬ÞkÜúb‚óœ<oJBÆ5ùßL A\'?qóË—Û%!ø!Ü8ýI2Z¿ŠoDXqMJð.bH1†gh)
Q1Vc‘©=+ÍÔ³ÞÌÄ“C³ÏÜ™„8Qˆg3§C±)I˜GJ(µ°>`aN\õ1UlÉòä þÕä¦xF±í’s)ʬv¨ôT?øt™HS6pÒ´ (®hL‚uàÕU‰dJè9a¸³ž]¬ÿOLME(wJN¤‘-jSzEm´-iS//òÞ‹IàÛÊ¥}ÆèÏñè5¥˜Y°ƒ§Éb#ªû›ýBÁXßÒ†ÃÕ
·ff¿7Ž'gu;Á§ŸÕí¤Z¨óº ¥í¬n'H©Ïêv‚JùÝNDó2a×$¿5µúEÐBƒ@`,PìÙäZhihþ„fŠP^¯(±p4¡md´¦YÅ3¹(Î$i¯ ‹h†¦žÐD´FyV¢Ð†´;óIháôÌVª#»IG6“È+Á=±þsâA—[¿WÏ á{Óíz£:|oÄ·n›ßxÙuºÝ(ö»‰½šžAÆüÀÿ¦´A÷¹Mu$Éñ¹zŒ²Åa¤ò‹d?ª?‡pnë«$t\Ø/ÏzæI^$ʧ[ÛSPZ'=É«›ZÐÕÆzn!¯¶šk'e!G $±î{Ún÷üÃ8|¥6>I\÷%vIêÙš -åé#ë&â;Tt»TluªPÝ~vBÇŠN׊йBYïŠNÿŠ@óe,†|WG$}ñ”š„Æ)(áƒÀ}7€…ç`ðµfLí{Z/ -FŽÅí0sDʉ8%h‚ùœê
6½“Ë+‰"¦sÏ]'X/m½3'ì.Ÿ¹X†¹Õ;‘Ò?Oœ¬ÈÚ¤-8¦Œ?ÅVQRp+N²ç$õIÇÏÇïÐÈ3g²°‹]mui|¢?£ÚæÐ¸ÍŸÑÔ -»*AÙœ.írH’Îen}Ø‹@‰¢œ3AaSCIV¨©çT n‘•P¬kbï‚00B²=±û†§o8ì§>õ0´ÃE~f!ÑP¬&Œ=Šaè…À†P -Ñú6}§)œ108£þ"0êW¤6!½1ÙNrë+àÁšXvº¡-¬°Ö4ò÷÷áÌA™”×Îø„Zg\›i&VW©Å×8t'pÊJQUJ¸*Ŷ|Ú(ñTµjS«ª¶ªUŸ˜–G«TÝftßÿ¬\Õ¢ºÛMïûµ¸@|ÕEnKÚd»¹µœû¶s%⛽‚$÷®¦´Ÿç^ij$A¤¦q6u?«0ݬ1
k8 -Ð/4ÝUjZâ›\¢1½;Ï‚-® £{ñók÷!—S"ç(¶ÒOó7µ…lf¢{½üˆµF½Š=5-£ò´O‰§Ÿr+_“Y¯—hné --»"C¤ù1š3Ñ£Éd|]kߘõl$‚q9K[pGÊF¸t0¤¬±º"§-J”¥×PµôBò›·4EÞ¯òôPÚ*‹œúiÎú«©äia-QF:¢Œf¥-´#b6œ enAÐNibÚE³¬ã›Œ3„ `ØÜèP‘Q:3“…aFÞÖ1{?UÆ>ǾN¹9UœjAr½d6ÕBÙ¹ žM)ëNä³cÊ£B!g[SîUdÞé2ª8醫=*ü˜0ʰaÈtºÍpÚån/‰.2ë¤Rr.¸Y਒)ë©2ÃŒnÌÕäœÍbÊVO³¼&ÅœÖWÕX:g”àÄf°P’À‚xL\2'NÂd¯0ŒJLLJéå®ÈyÚ²gÄpæ -äµ6¾…š_‘¯ó˜óVÞŠ°Ú³ŸbÀ¯ÝБa ò)”M0 ô@(B³‚CXæQl|ʹ© -Ò¸J€]Ù’ »ÀÜšþÄøGÒ±²U 9¹€“ÃÜ…–ŸÕ(Óm–if8Ÿ”µÌ(Ï4Ómœé4Ï´
4bŸQ[L4Fš-fš-†šnSM`¬Ó›>CFƒ¿<£«ñ4’nFÒÍHºq7#éf$Ýp5ª"ç‘ÎFE2L»BWçïšêqØí¸´í -ÞQcSÒTÄývyvhn83[E‰•ä%æÄú1+ëȬ);®ë%ÛäÆ5¹
Dm"Í3Vhv¥GŸrë¹b;’p¸ˆ«T`ò—.¬a!±6,)žE¡‰ŠýžÃpW?Ô5t5Îë©1q°¥"ÉŒÃCÄNå[¨šÖ)£¶íRR¿¤e’Rl•2)ß.U{Ît~…’fu’´«:IÑQ¤Y¥ÄÕ)‘J%®Z‰W³D’ª7Qº2Sêí-T89õ’Kå™F]iT}·{I¦j½V$ùŠf§ã²’±'~Eâ¤ñÇŸ²»Êœ„M1¹9·
[¯G\T$²ªfç²ÆY_ð §„6ÊBI=!QxJBñ]¤\ÌUlýOŒ÷Ia=O&öLÌPÞ&•bDxbÓG£`g¤ )]2zøÑV]‰<šV†2cßÝ…âÀ‘¯Jl„UNŽÀŽD©m>à9‡[-8u$UNQ/¡( -Å´ÂKì§–¬øœõŸƒêæàûU+Vj†¾åig˶7²íiŒÙZPÔ”b›* -dÊ)tÉ„*E6DiwD‚5ž)k=kÚÏBšoC³V4/:ÁÒ”gKKƒÇ¹µtÕèÆ®ØFN¸ªX»ä›¾‘^ÒU—vµ#¶Ñ$kTAy5?Ûô`?òÀ“\5œÉ;¹Ëåh²9Mªó*7ÕÄÆ-\¤¬‹¹¸™Gsq6‡sq:ŸX×óÊ: K£¡+ó™ëüÖõ“no&jp¶a0Ìü 5àúe²ó Ç\äEÞ6¡ÐÙr]Œ›sU£vÅâº=ño•ͺ;Ê6¶°Ø®ó@£ -Ò" C!2„É] `é…84áÒ‡Ì.Ø$èT
-Ú¦ûq‚MᚪüŸÚk3¯ Æ—Øi±×ØéHy…fS˜ ð‰·†juKôFžÏ;±{dAŽ‘ŽõŸ‘7d)Y-BÖFBŽRü9å`#NO1Ë&Æ(§ÿÖN0LnsÀÏl@VBéßgŠ…éX%ñ;‚æ}Q è„Ò½§”óÞdy/F¤.)4¥ªsŠzõc^©P…=”œ)Æßb?³‚Ÿ<Ñ/Oég›yÊ‹H«½æ`å5ÿ§°-wMðzÍ?~ª?„ÇK(e•Òf^›zæ´ÉWOO3ßj¨žžf¾;ÔP==Í|wL‘:.Í|C³¾7eAª˜-rF•`¸ xQÀüõVEÄîfýGžkXxd ’ð,ߙЋNN6@{c®ÇÆç?úiðÓ݃Ý#fNçIÅ7gf¸ÀÜh|ÕÅ+>åÞÓXž¤êäyà²o¹4MSÛª¹B§9` “|L|Ù¥Uã[fÅÂoŒË¸v|é´5H“Î5€Ën
¬åÌYoý¶ -X®m‘f~¬`Åè£<BÔ"ޤo&8#VÊ0Ã%6•l3(l®ð8ýêò…WYžQ†ENßãg'ö’ŠùÔþ˜8Ã0ÒÙmåqÜRÔx}Î;ç@ÒFV'n-¾ÊdgW>wq,M#ªPý\ee§L<ô}ã~µò`ã겕¾¨V¨DH]’êÒ—…Q^°D˜òÀ‡fºƒ¹ ßüO¼©ò`¨æz¥sËq¶!©h¤ª«m"ƒ”ò¢WµÇ7¦låÌ"–
›xpÆMM¦6Ûá àÊ -~ð1D€#”‡$|@œ
»ØŒNFCu -a„ii‘‰e=p0ø„Óø*äºsŸ‹:–è^ô¢3å‰N°°hFæ‡P”-!Ä0Ì|Ô*¹gè°«ÀG·SH€ÝTr눯ö¢«]¾\—Qœ–jUm¼•JöÉXX僔î^Pae£zhVÔR -ZV; ‚¦)§;¥(,SN×+¦k#’f”/*¦LQíh¤d–¢M¨+I@˜"à`ÿ†ó-ªã#ƒw«Ý‡4Ù] ŠQA´ªQõQsÄ÷ -( 0€‚ -;?H¤ÃÚîrûp_2ÎÔYD.î9jLŒ~sÔ?÷<å|×vtzöü¦ŸvÿibwÖ-vÃegoÄîAìÄîAìÄîAìÄîAìÄîÁdP@á©b÷ßU+KœH"…ÔÖSVJHs^³…-`)›1Îåí‹|ùûÂ~»ÓHá§:røE®\žãxˆ˜¨°p¡ØÝÆ9H§6bg/ttÁ‡ -œ¤çäœä'ˆNØIÉA…!DPÆ"eá¡
|HØ•h†\RíÕ‘vYícÉ`oÜýÒEKú„u{V2Ïš‡;—2Ã+›;èï¡D–,ry{©Y~p ÛQ9í~Íù·«^º4Õ8øa®´®ãºÁ·á±0ÁŽÍÞ•¢ª¨n æÖÛÍ·E
LL`ƒ+#žÚ¶”…æO<ÙùùbŠ^œ@c÷^ñæÛœÞöÏÙÿ± -Е¤×Ðþ®š )•<D‚ÔQ\$Ȉ©èʶ» ,ýÆ´"oá7‡½ª:Ý@˜Û¤âÇ<
9-¶) 2e_ÐŒ‘+.¯A¬¦*Hlë‚àzâÑr‰÷iÕ6d,áÄÒVYC·Š®ZHÖ¹þàbô‡êPÔí„“–féàÒêÑ.wQuŠ¿h—»¨:Å_´³4ÅvQ/‡¿_z|Oµ³üøÞ,þTÛTŸšZãŠJÇT>6£zª%„©,ë|±@a -Iw¡íb.»iOj.¿öô„e8›S¬ø¥ÛùBêí¿)óÿ" d^ƒwʾ̃²Æ×Sû¿ì± -\’ZwŸY²é‰u—S[¼åœx´Û=ÎwóÝã&&÷›K@:ÆùÎq‹;ÉyÞ_±ÏÚ9š¦[¿ZN„ªËëˬŒÁ[Ý¿²N÷¯Jy¾<ΰÒ4»´\¿Ìä¤ ïÝä;»ùIf¿ì8fñxã©+öq -go€¢².”nòž{Skâ4yx9uù9NâG“³g/Å™”4IMç63GN™K±\Ïœ²”†©9¨Î j”ìI¥d–)Ù3á’=igÉž)ÃüÂ2_ÅÕû4—ëñÅm¶¨Ý´ÍÎ\%ä:ÔN¶ã€(MžCR¥m`¦Éwji©vLíÀ/áôÔa%^šÓ3;w† -6þÕ9*ØøÓS‡V° §×RÞ$‘Ñ_”˜Øˆ2 -VžÙÉ.œ«Ã“v'½ÉµÄ†€!À†!`ˆ -CÀpüÏ0D -MÉþØ{·{ilMqw&»[ÂsÛâWo¼êrª?È7{âíÒnoÜý{ëŽì¸‡8‘ÐAÎ`}îη3¡+iw¢(/Ì‹Œ?ÎÓ~hÄ"‰UæYœÎÖeWÈýI Ïd5J»ŽHÚVBéN%”Ó8„Ž)9‰lqüSèŒæ›ë_ÃIIu–I¤°Ù5Úî; -[ø©_²…¦¯Ì„™(Lro™dbä3£]šZgç #.9™Í{/ž7ìkãy×ÌTùÊùÍt¹VžkU«Â
ù¸¨Î7í"7zg›ÚyT©F‘›v™q§rUÍ*77•ÚîLµ%>×:RùѹΊCüÜþ,œ“sòj¶:g½NVÍza>V[\¬:‹ó¨þUÓçª=¥yÔ¡µyv:UynUj{mž.UÎ,àÊJØ2Êx÷5$¿()È܃+l›r<p©LI¢{gÙ°w†‚àÉ]}4¹0ëây³€çJÐ
%è†tC º¡ÝG*AׯÂj'óÆsrO'Ѿ¼‹öåFt }íhß@ûÚ÷‘h_¿hMßhñÓi_ÑEûŠÁávp¸n‡ÛÁávp¸n‡ÛÁáöù[
/ËP@ap¸ýK
«‰ÉÊs8æ-gBæ›Õ] çÌ8ïu “ß¶]ÛuŽ_©N÷9—2ª©rn¨÷;Éë¶Ó˜ŒH7ø;ÏiÌgùB÷±¦Ë˜ç<vnG)N'¬NCmûƒ\Öž¦sPÓ5h!CÕéäô€M× †îõ/wˆ9À¦¡^Ìr -Éâ´BB:Áܨ%MMYDYI*Á4‹ªrn:¹§†©ª.LUŠ©ÚùNÛ‚Ñ^·çIWug<íÊyJr¾ -ÓžÒ9
òž*°¶Ñ[ÔBrºÜ<ìây‡>¨>êË<ŸSÏØ¤:Œm¾¡mB¸ç¯qÓ<1½ÚÇXmGXûPVi9FåY8ö{4¶×îdqU—åÈÇq!žëÂxmÌ×…ë®$lm´ØFÝéÙiæÍ¢Î-ïXIR–h‰ <–Kr’R\Ùª½Û¶`ÄS:ù«á.Vw€ÿÌÿps&(rç´Ó%ïü„U F 2Ç}¤ÀÍÁ9 ”Æ=bšðÎ4áéYŸ˜ævpÂsÓe¬ÏDfI ŸÕQ.Z<„ìºã0Ÿãq^ï¢Ñª<sª´˜¹iØqw‰3
ÆÔÐØ-Jí¤‚ú<[)¦l˜Xp¶gMÖ„r>§dyÍI}UR.*QbMÉjk@cÓŠ¬» ÙzS>¹D¡L„(îÈXe5Ð5çàŠÉNeTwb¥š£xÅ6*³=3µ»bxê@Eh|fž¯BLi]—‚!ˆF]hÈ¡³Yr¨HŸkÕÞçÕÅ2±S;-`Áž>f÷wÚßk·Û¾èìï9–2±ò3IÐ&î<NÊ?RÆ÷̳ -CnÈ^«<GšŠ]e&7UÛTΦ±™.´üp0‰Ð©"îÔ4ÀYæŠWRÌéž5½wwÊõÄú)%¶G#š›ÄЙ v5Ï>Xžg²Fw½O½wYãSz§øEÎÁ\òAaß›ÿKŽ-lÐ×–ïûbÿ戟3m0ê«ÔÑ -«=àÔiꪎpšKëü#@.é¯qrq*µµ—VÜJn…ˬ¼èã'äÒ!ç3ä'ÞZNÙòt®@]Xœ®´'_0`†4¨RÇuêTXªÎøUµýš^@M_ ¦GPú¡:|ƒÚq »¯PÃcHµbCš"íÔ»a´H#fD…¡#>±Ø!oÓÖí¬ùqHèoèñÐåƒêÙºÛ20²„ݵ?Äó¡å÷Àî2ä0£Ú3ÏÝî»ýGÃê±jËÿÛ5“muöt¢q '£‰3ÅžN=úž:¢èjHÀ¨ÐÆ€>*ë™+áÅSb,æLÎ]w&^<öZçBeÕ".Ž[úœ=¡RcîÉú¡b£ -Ät'‰§›Z…:ë̪9&×f¨£mäÆWã:ìêé´á.;ñ0bÑÐ ¤ÚQݺßnío ûØ¢ê õRÇ¥žßª ¶Ž³ê€äóݾ³[²Ï+Ï–÷"T -DãìYåhÍBÉ<#+–K¸ÚÈŒ^•ã²ÈS§d9OwäÚZ&YFIh¬ô}Æ)°N¤Á‰r2¸QZ9e±Åµ®¯¢gp -¼¡§?“ŽèO‰³‹Ê2§lµQ™ä™è”*L K¡•E°}d1<³ˆ=âiýÐX`¥SmfQ¢} Ÿì¹›Ve”zKg9êÿÇ€…ËÈSoezHÃÛÂ3õ÷¡TfpáÕ›õÏw×ë‡ëõÛ‹O“æ ~|Ÿ$擟—«»õ7£/g×ËÛÍúj4}|½ºÿJ5Þòq´n¤õö!•Ù8‰t9*ªqpƒ~¤sñ€ÑoÒÛÿ/ÿ.þ>JG?ŒþëÿD£+ºþÛ/ð_£Ë[w‰{½€K'×äÖíÞðÒºãöÛÃo?p0kšÑO&ÃN‘U š—t•T -FSé|–¢/<>.ÎÆÃkI<.á¨Ú{_tô‡×Þxדtx§žÇã")ûÏñ¯ÉsäÞýÉsòlŒL«½Ÿ“Uã(ƒõñWYî÷¯Á9jÏnö×|ެ‡ÿSÑ1N¾&Ïñײٟ<§¹ø]Á™Nó}€¤“±Ž“½«û¦cwý§ø#ç§ø—ø)û`Ňã2` /Ǻ”ëßï_{_tô×ܺ?Iƒ½Ù«²7þ³›ýÉsäXúg§(ÇEíßÛ"†S[„ãiö×|Žvä9û`UžãŸ“fÍçøû#ÏÙº|MžãïE³¿&´Éý>´ùsghó/1´íƒ–7†t– ÒI -ݵ†à~RHæA:¦ -5yœPtoU–Uf2V…„Ñ—ÈMXÙˆ4ú£y«Â„ýæE› zX£•Té*°VO†WlÝ:MÝþ6Áj<£ßqq…˜ã˜óqT”fºÀ$LÀ¿ü¢q¹£œö¢ÑG÷UÛÅë°“RWf…Ý÷år£„sUÍ‘l¹ì:yÝÐ.ÀvMåv{·[Ñ=ñ®Ezþ¡ªÑ—_~ûÏS¶™i8]Á!|E[ðŒÇp[ê]60šŽÓ¸Šq9ÊqR•©¹Œt¬2«ä_n÷mvÌë=‡*›ÆŽÉåÆª -˜°€£:˜Ñ¥iÀkéÑäý!—f†«‘¶•CâX'ySˆ:ÑØ¶\¶\Wd†ÂÏ© -ÊECl8”qˆòôP¶7l¹ì2?¢Wi”¶ Ø -¹X¤©·)ã`ÀÒÚî£0'2ÚGðaUçÞ>æ%6³@RJÅóÊJ‹Â58óNÚ'V8专©y#H·“1SÍã‚e'3¾HJv2.%™x;i.fe•Ê>Æóàv»)“·…Ú\AÜXÈn¥HqühÚBa7º-ŒyJ@Ü2¡2±@NþÜ[(Ó%äú¶%Ä,abC̆•Šœ)[ž% s3qQ1µ‚E²ó<73„~áqä0¦°_±ðcp+ƒbùyQ™òÅ*µ,)Ièj™ýF¾«Œù¢ -ËaÐ6fü=R•¸}ŒårbY媨¤Oy>î£.øùE‘xû˜0M¢H:dã*Š+pæ}Œ…Æ8ä¹…(o´åUaõòЉ!=Ùǘ/¦ed/¢%]$ÖîcZ²,‘&©ÛÉLD©$ÎìNÆr1M·“Ì»Ãå¬HíNšÓ½"”cZ–æ"aS·“ZDœ8OíN -Í%qçœ{'å‰UIæë³“hÖŠ]ZŸ“!Îü*l©!„À¥VF÷c_,ü÷d‰ˆp¹0ø -žD —|’éb—ËQ\0›¦ZŽ4p©Qưœf -9¦âsøR: ±ÆŒ ªrÛA€ÇÜuœdü]8Œp‹8—^kp΄γˆ9ÖÌØ&?ØNæ/n•øËU vv#…‹M"m/– 3¬ÞF -og…ÝÈTxÓ<K»‘)ô</3o#cùnQÉž•UÅíp#uÎ,sœÅÞF&2Ú”e'ÜÈœ!¡Ê´è'Â%8ó>¦BOJñ‡ÚGà’„ádÆØj.°†$_ÎYäO€+¨˜6’k¬ÐÈ
â¼=G"«ÎßÍt$’KUJ§€ùb¦µ ÖÄfš,ff D¼\³0!€kE*pp -ŽÑòêúñPwS=îW"üqŒ÷§ƒÖúô1 µçžŽÌúo2ûd|R_÷‹c?Õ#¥W“9å¼÷k[Úþ`—Ú‹ž¹¢þyä9¢õ?ô–œÿºgä䌱úýšÙycõûåƒö¹Åê?¾öŒ,@`ûu²öj*›div}ÿþfy¹º]~X¾ï#m:‡ïñmè?Ü ¨oàÕöÿùÏ|¿`¬ÍTÿ1}·\¯W7/W7«ËÃ57¿ôjZíI„“üói“œôj’íI4x†#|ñû•÷â„1šbÔÎKøÒ}$DGI·2‰þlÈéâQ¿$ðSU<ý:.§£~íK›Ñ -ßýÅ)=ü‡/××·Ë~ºþ¿¹¾¹9XµZý»_Z(3úp§—WW××ÿ:”xÜH$ëÕ´ÜZäayw°ÇÍïË?÷`‚¿žŒ˜ 4çÝæöYº2›‡“Vìò‡ÍÕ¡ðws½^-ûåœëfÐІbA–C)K¿ -ÊqöK/:¸Ã<CÂt‰íÖøìÝa.ûeå:Õ¦oàu‚;Ì'tæûcƒ;Ìàƒ3ë—ˆ4¸Ã|jî0—ýÒž¨äé?õk_Îí£w˜…ðû=cýÒ œŠûz†1NÀ}ýÚ—£Åï!ÞÓ¹õmï×OÙûg¿}³À~ž»ÐÇ|§û¢œ×›åðÀûž¥˜;¡ôO¿&rZ2Cݳmùl’E^üÓÍæ¦îŸ#þ´ñCà!سJõT>~© -Uùö|zƒæ÷ ‡§3r÷”åbÚ¿)‹ïf¦šæ®¢ü—ëdf×T^ü«3{‡r†ªCݳŒk¿ü÷õíãáªð¤_zG;úpR«È1iUûÖè
ÿÜv¨£ø…>ÖÕT½Þž6‚¹¼{³¼\½¼\,øõÇ„'wÃDòâ -2noàöçöúZúÓçødåŸ5jOâ3âeòó?ÞoÖ«Ãw°_˜¨=‹Ï„Ûý,Ï
ߞߟšþ¿¯xt…„O)HÕô^íÇ©¡—в“±ÁòîúáÝíªoÙ~NÆ -ÏÉ3òŸ{o]<D¯&‚ÃnLd'M_'’¶&²G*ê©ô„ÃnLäPìÖ³‰ÚÙ§ý8•vö—Gèç3ÃHgÏõ@:û6‘§“Ξֱ¤s(ÒØ‘ àSq} -,<û]é1í:Ý2wÄn™ˆ>ô¦žœ‰è×~&Õ=_>¢O;mÏLoŸ[B¢4ƒŸ -ûÿE]Äù8‰t2Š‹qY¥£[º¢‹8%Ñ8ª¢dt§ã8/òQ¢ÇeYÄp!Gy‘ÚSEWrwå…¢›rwõª+Û+¼«ª”çBL&+GÙ8’»¸èº÷…zó˜)޳Xg#Œuœd0
{F'éHgc]åÉHkó
Ãuüœß_*¸R¦yá¾Q4z(ÂgL•\‰3³b/ܘ]ÓS¨O.}Cž*ïá©<.û´ÑC>cÚš+®A‰ÊØ›¿½Â£‡›tš»ùfãÄ|nç”gQj¯äòÆ3¦J®¸ùÛ+<zéSf'Ouó—qÉ•¤ÑCÒxÆ´5WœÿEsp·Þ%ÿ&Ü”/ÚkpÃÉ<0¹h®ãEóñSuÑãïOÃv,ó¼h/… ýNs9/š+>mÏœ–ƒà0X
¹"Ó È,¼Å ÐÖ€fé}%mô‘OÁðo!äŠÌ@:µsäçzË Cs_)}ÁS¦ùâüànæyU£xœ¥išà:”€)áÕ…çU8Äjœg•.¯Ó´‚n€åG ØÆ4Ë¢Ò»‚ÓIª„Æ)ײqVÁ‘¹žòqé¨ÑãhŠq’¥€ï`°€Õt¥Sü0OJø:ÂK¾…%"í®]àsŠq‘”1¿Éù„?˜9 H]ætoŒót…ÐËý$ü1>G^à&e è)ñî—Y‰Ç0Ž’\ã|ãJW¸„åQkI§ˆ}¼ÁæÇº4s—ù -GgÎ]¸)бƒô¥ Ê?‘Ê•KÜ®4ÅpßJÇi\ÚëÉ^ñh¯ÙqÙždìÍù]ìóõÅ+`LÞó‹WÂÇó—üÜåênY -ï~efö‹WYû+¿ - -x36 ¨5s|w¿ò–^MÀ4Ñ z%Ÿû_ÄÏñdªèœïcÿ>þ6œ3êÿ…òžé}Ço*ã|¡ê×J‚7êw€M¾üÇ¥ «È>W×+Øæü+ü€ -Ž:rð@™â8yEäoýVÁáÏŠÒ\¸7EQéQQ( -y -øÉ Œr ǤÊR"W€ã1<AU&fÓ´…M,a™U•÷‚?ÇÿyéµÐìÇþ}ümØHï)/
"µcÀA&nŒnS<ç]‡»ø -O -éxh+:ÝIžvœn$møƒ\g\#1_ùÈ]- -`›á°Ã1.`¿ÝÚ[41YRdÝ·$À-àÙ©/~J}ðS@Èö¸Ÿú2À@jGt®p—€h#?xË\4 Ê–»Ñļxßz±muã„–7Ä¥y}#dyÒ(¦eÅ‹ -rNäÿbq[¤WÖ©Ñ*þØëÝcã -?Ñ«ñÂ}Äyaâ¶uU¢Â‚7ƒX£ÒVñ ç3HReÌÈ´#:×Q„x<á -„æÖeg)Õ©6¦`Râî<å±õáEšÌY‘ð{8´dC2u²ñyÇzªÀ
ýlèã‹Q™BFôXV]ùùpˆd1ÌTº.ÊÜ|RŽË8F”*"ŒÆ—7öc¹÷o–nñÍTÑ»ˆR› -u_i‰UeYGbhÑÑò¦Œú7]”Ò1Öy{|WfcJ`…I©<ôèÉWð§e$§-Ò”±8ƒ‹x -U‰:x`¢Kå`]hïŒâAñþT‡É;¦tмs’:p}ÞA3&0rV@žIø(£¢†£«#så7Ò4>s¤pÞ(Séˆ(_®J™–o*`ÞãþÇiUh‚po¹ÍE0·êæ¢]ú‚ذäÿgï½²ZIš€ÁÙ -¼\ôG²êìæ“âaçµý-í›ØÍ²mzÒM¾åBù>ÜP3ÞN!°?ÄKÞ"Ûób,ë7ÙVФ›²ë“Tub0EX‚.5Ú±VF±}®˜µJ‰Ñhý›*²ÈJßûZÖ¡EØ_ùhßñoéÛ@ÅO4G»¥@}Õq£F2TÔ¹Z*2çȵ<;9¨Ðå™hæŽÂìÏ|¢e>Ñ - »€ô)þÛ~g8y¢ÉÆRUÓ¦Ö¾&ÂÞöÃC½;ߎ¢V{ÙëŠrFy¥ÝlsÛ×Üf}@m -æôµKî£|+¨ú˜|böøTϪ¡><µ[ð6?Ô¹4ï }CšÒ
å`”yMq=Q/ >fçòÇŒ÷ÑE—²Ý¤g~XIÂŽSJ.—=…ƒ-uÚc?ˆƒ±K¢.pƒe™Qø[8¨séÉv½ûæ†)?|«sêä?2øƒÑ§÷{õ9¤ét»DŠÄð/Cd?ð‚€Ïºa”ÎÿÖlãU|ýry™æ™Ž†w)„e!¢*…ŽO)¾í"t‚¢€¡Yc×<˜hfȇRœRÄ`¡&Ë»‰ÂE*#Ñ cifò8 -»'F¢
vü·óÁ¨¡Ýð-¼ DS²÷”1º
E^1¿›S‰ZÑu§ÊýnwÂr¿;PðÄñ"ÝÑÁ!
Ó`¨„†’åc·ž“ŸZ£Là-aœ¬º˜zÑ‚õÀ«Û òZЯn¢å%QÊ{Õ3NÜ“×Ч—8ðœÛv-lÁ»58™Ì-’tgFfÏ2&ùQ$·È@žc¨Ô#ÐÈ<ꃟDQƸcü-„€ -2fÖgdâCtÃn#LÐA"y!vI‚.M
ô¸µ´ˆˆ%—©€]7 GнI_#QìA™`h´Õ‘EÄž$°Ð°Ä<šA‘8ôˆ0†Æâl`è´`K‚Q1«Hõ¡!Š^‚C·KU^ïéÀVt•µ"ËTÀÉšõÄòÕsÒײGTÑ•¦ÁÐÜÑЈÑ;dŠâbñ¦ë¼
Msbxæ3v¦õYðyR¯«ˆ,J¾eŸ‚‰ü);poýÛ÷GYñ®Uk
Þ*$ü¼[…pK4™ôW(º?
,e¥ãæ:»ù—@ÿ¢%å)~l“ú«Œˆ|ñR -»Ày¶¿q?±›µ[µû$8OÉ@å¡Öºgâ2Æ= -¸úí¿€“¥I´ö(¸
üð}0 =EáÁêáÁ1öɤ+F -T¥pc]‰ 'Tf—x肉–3ŒOŽƒ’¥¦A—xàEb„0¯Uº3¯×ÐUv‹‡v8r&UW$‘îÁHRìG1p³˜úÁ;”`“¨løJ’BÈ}ñª¨ªºO°ÖUKbÆSdŠNÊÌ)üè+ S j„mHÂB Äâý8˜. àÒ T¾*|Âñ¶œ.@iN(ŠuæÚÁT„e¾‹À<àt‰¢KmIé&»ƒä¸F*fB4˜†BŠÉ´w†ãÒtH“HT#ØÝVvÁ‘N±"ìAC·è<*ÅòBÃF#,F\¤¨%¤7!¤š$²{qPQœ"Ôtd &ÔÄ((· -¥Œö/Qø-q¼
çÑéÅ)á€8
Ø
ùyt vF‚üŠC³ný×Êt½£Ñ0ІgîQÚ<`§#ÀÆÁèfÙóõYA³C5 -·FSÈé20ŒÏÖ5…5+ tú%¼_+™¬ˆ±ò²GºÖÉ󗽯ß&ÆžKž¯‡ wI¨gI$»ë€y‰Ým@:X@6«Äñ~W†,P¨uЧèeẬܳ]ÌB®Kþ#ƒ?ØmUÞìÕçp…üèe’Y[AiŽ2GAö -5A ~PàMm0BªÇ@ߥÈh4t¼@;“‘—jïJé°º`œ$~=t-.Nš!óg@…jv UôZ¹]x}"+"=JÕ}vhÅé6ºè»‡ÓÔtÃ) -ÓתûµSàéÁSÆÁpÚa`Úà÷Fa¡ÆILÒ…N„ó/² -O‡¼N,Û -3Ýð^‘Ž|c”-×Þñ&p§€yáHÍöTÒA£6TO;NïŒ%•åeNš§!Óvؽñæ×â 9•¨†Çöýè9#bÄ+‹„å”Ù ¤@X<K¨êŒS6`ôh û ^w‹LŒS@žÃ¡H?º—Ù0™ÏJ6T§€aÊPLÍS}ò 5ÜVxÝSqÆ)›_åG€X+R„_ºj÷d0ä4n¯ÛÊÀ˜¾³è\Q†¶³èœ ¸>¼K·/U¼‰¡Ð/á!dTsJpÑJB<‰_…^3hÄ`îF^€ž5Ù9êAE°NMCd.UÞŽ§ˆwF>/§L!_Šs~€òÜãQ§;» -œn0ƒ¥],}Év+¨&«‹e Žœ‚âŒ
‹SäÀÏÏ f -"sü7fÈT@ ÊhB›ÞîeÆd90
̅ר’¨ðkäénôR“_^ -“)´ý \E´¢Ù˜‘È81ÜÍzI·âá¡Æ ºÍ"(¤²ˆáLãÀíen~Rh³ÄäÛ(z§qŽàC¶`*4TW‘C(t˜ô6Ü×Á¬tñ]#¯é˜±^ByKÛ›Š‰‡à H¦mNÛ -p $KTËÔ0ÓˆLÑÑ,ÙÝø–Iû%§„„¾ -2$EGbp'`ñº3ƒˆMšbÛô@œ³JG@:í¨1•´Ûâ.¢BdÈ o£¶C–<ëLq¡¸|FèQ±ÇJÐÉ4tÿ‘Ab_¢²©«,¡sßÌDT܆Cb“éh…)Š2<TdýÛ¸Juè,B3O•÷¬áN+…>²a°8wvðx¬ -€ŽÑ1ìšL3º³³áª"cx¬9âk æBc!@0Àæ%Dhœ¶
€¹Ú6«`N×qœBß<õUÒ‡¾Ã;ÛAu \ô˜!„˜Î”ðÅj¸9ŒJ‹!Óû÷“…‚"ˆß{ßæ·Ìà¯Ç5Ä>žÁvûÉQ†Q1tµ—f˜LÕñ(ô ëx¸ -iM
†)!ªˆ%sY -Õ§SÃd¾j*"Ê,¯Ž±»¨„HhàÙÀ•f( -ú´`â”ÿD1ÐÇ+Ó¡2RFeŽ’sG}:ªã8“M’¬é,òK”¥‘ŸšÃ4±UˆýL 30°šL™X2 -¿Lï=DÖ0îï»í—N䝯Gè2iƒåú #îׄIHy’XÂ,›Ã`ž–=ÐKtF
·Ñˆ¿€8ÆøOxVÑk¡Å´¹e0wÚÅ*UÄ»TñÔ") -sS‘K„ú)0ÐdKUfo0T(Þµ‘ï_©Ž‰¡—[³Üá¬Gxô –q.¨3ÂSY-™?𻇅˜S‚"l™‡„« Á(àYÄCÈF4z¾!Ä}³¶¡P -Ú˜¯MÚ!ÂðE]ÒÐmÄÒ(ln/2çiåÓõMw^ɺ.Ü»¡Èã>Å£& - -ØH€XmܵZ³…öênÆc2³p«IMŠbÉ=™Æ05r7’·±“G¤=HIÆheÂ×:p(ÐÂØ‘ýþTÍì:þÑ®
…ïÞóø<ïw‹
‡¨’§p -R¨Ôo &vmÇGÈ Àú‚›ªì]MdúúiIüíZe Ù˜“:=°ršËõTq²þ„cb,ã+Àpw™_@Æ‹”ˆM'²¢êaš"sÈ*ïo)ižËv«èým8ϼ¶mCEt4œ<¬
쾯»€Ÿ9W(m¯cÃê428\k£Iìê0s¾—òZ*VÛE˜mé1è[§CH’ -‹f™£cû&¥Ðå‡Åñ´Iàþr9‚ÊÀB÷±A$±¤iÛ+ãEa<Úƒ†Z†BixšrܼPéX8æËÐé -|ìBbhPBOӢɞ’¿[÷ÖÂhVÔa0˜‰©,XÄ0tæâ%xÆÙÐ9ЬÌ&“ÌŒdçVÑñô7õª‹¸7&¾ÑyÁI˜ùTGKqeŸ"bî‘þâlS±Ê‚áþ–Ó¦9+ß.V†òøý´„Aä‘G5‰’›“$ÀãèŸÈ‚û°®AN@–"ÇT$2h¥ãb@÷™Œ‰:Eøcúj´>À:¢0hž‡šggVðŽ•Ðõ‡]€1ìt2€å7§º€ùÚnЬ¨ÄOtnYÄZü-Ô'8Å$9¯s.%±°x2–@¬Õè°‚€9r ¯P¢3¦=mÜÍA~iFh‡±S#š:£bŽ?ÒãÿÇùC8õâ“¡¹a…žºÙkp~°ðf>ž#(ü–YÂÛÆæ ?<S#°…-¡9'ìÓ´½yì¾aBÐ%‡FáþÁ“ÞQzc å -1ÌÕV4ÇuƒWhrla™Ž>ÙП»êŒ.p«æç¥3Ñ.‰²ø%ktXDUÝ"Qa^T,ÑÁb’ˆ@/‰ŽY%$#"‘*‹[¤2"‚’‚°Ñ`ðÐ>9U‘I
Ú4Aù„~_Ì`‚›É?„¢:9ìç¦HQUMsJ*‘h*$P)|Rg^Uv'®a'"¡("*ˆ¦l°S -E´P¾(Êœï^¡€ ¡<W†™‰%ŽŒCÄ›ÀË.'-êÕgðü9tí" ~5 -C9<b`áã@Sx6=ØÎœ&G7h ;TQ3é4•Nî~ÿ ævÄCìšs<(Š’%C‹ÆÀ0Þ¡Heû$DÁý/û¾íowDÏЦÈTÜ7®ÀãˆæéîYÀâ¼àá¶àS*}gtL»¬h$<ØÉ(÷˲ÿº·ahžªÂèXö-~(‹vÑ‹«Q<ŒMaÈir5ò°iÿ€¹Àp/‡!)Ä(³“´-¤|÷7Åä§3#äí7)%Œ!Áä×è,»Èüɹ¡”
òI;ödC
¶OùG5‰Îx*7‡gó$:åvJ.OTx=°Ña'Œ†yÑÈÌ0ÆÆf‡gí‚F¢³‹A𚺮ռ¤qaš¨ -aô…‚ªj‰ -nitÞÓÐUæPÆ9¼ ÅDK
õ+<‚‹Ö;å3ý¥¡0o¨óÛ4Š¿¹Óø”â”NþoØžÎôú¿Gê"‚«h¾b³Þjö%_§’oY¾z³äÍ4Ù>‚º™G|€¾Yá -TÕ¾SANïü¨zx–™?êCz!𑤕0DÄÓ-ö¤²Ãx¦Œ¼¿"Ýz%tH†|½ÞGäfžšaoaoëxˆ‰:ÍÌØÇ°)•Ø^KFÙéÙ6ž,bÂ}àè–^UóèlxÑ‘`ÖÆc®S5"¤¾þFÓ¸6Xüx?ñéû1t Yjƒ·Sô¼˜Æ|8ô¤ÛwÅh¢“(ç±Òt\[c·Qêa•˜ÑT>:M0ËÂA‡âñ{åSõ¹-ýÌ0‘y×ñ1ìm"ìm'ØÓu†=ÚPQ#ªÁÀí×;÷ÂËL¦ºÛ¹ªiN¾^¤1DµÒj”3¸éˆÁýW˜`|›J¡¨£¾Ás¾&;óùÑnâÓwƒ'ZÙeòýTðÞ–ì -r—bà•…ÏxÇ[8L?$r
Ñs¢8VÈ„KOTj;<µ6-knpP“hmÔµªÞwEª÷\ˆÝ#1Köd*AaDn—)<
ÏàËÅ»”3ýË»®¨ÍÓŒ[Àûµì -û-°Ý¡Meh²g/ ÀϾ™Ü&ÂNãŠiõ¬1»¨°Ú¾a½ãˆø(ÐyE™ÍW?²Uæ!PÏŽ™¥ ý
q'ù‡û‰OßÞ»MI†h`XIfük5e‡Ê}UYÅÇÇ£fã¸]mt¡¯p˜“í}1sÔÂ7{s\ëÁ¿ÙÂt<Ã"Ë}ñv¯Sñæùg«í˶KV;8ù½Läkµ*¬ŽV¥Zä5Ï`ˆ«>É×êF|Ùßòð{¸*¬"hÂ[_XDUj"]Ëwß/SýSôò÷×¥&F| û`¸.ˆ¨Ä˜m -„À Ãz˜˜CöÅöãíj§[³è³Lµ -¨j«ÕN´lp¸…\7_|ý@ñ|§Zt?g“žë¶›¯–/[.w,!âøïù{µZE›íH¾Õ‚!3ÔÂGüR_£¾ —*£êµÏª]jN¢~jÙ6¯ -ÖöŒÍæÎâwþ(w–¾¸ówþRƒ¿ÔàI¼VúRƒ¿›ÑÊ_Œö‹ÑþŽDEsCÿßáçЇ9²þï`É_ž‰ïgÉÊKþbÉS°äoÅć©þ;xª0ÈS_“§JÿOÕ´_KÍý7lû0¡Ð˜}ékßÿïS×mWJ¼2RsgŠ{DÁ;øpBEL†¡8Éó -\1ï´Ô÷*i=·-kd'Wý…ùF©Y¯þe
dÃæ[í"0^|©ðÔÙjã¸Yeeƒ\x?p^Õ›¿á›ngxxýÃc0Ñ›LµÁà×%o;»v;&§Ñ«'šª5vŸé¿À±÷ð®vßNù»oµÿbÓ_lúdÓ.÷å_Þ¦¦ãÔ?ó˜Ì³{ñ¬çîŸ-Â݈Ël¤;
†®…ù¯ò¸,ˆªgË·~³jÿ±ˆ -õøIØã¦>¨>•ɸc±ÞXöµ5úG¨Uë¤R8‹®ÜézÝK£Ûšrý\Ã÷—
YE?…©‰šl²•Q;séÒÜäôÞ¯n:v _šÈ -S\}øŸfÍV» -ÛùV¥ZôÅÛ½NÅw´=yÁ³¨6V»ê‡ÿ±d‹«‚ªS¬7ü•¸Q.Ò§øCÁ¨ð §Þ$<ö&~,˜Àá@É*”ðîÞcJÕé$P}'ÛèÛyq^_äÛÕ<Ž–Âp âK×òHËçj±Y²ÆF*¬îd/§ -8r.ÈnåÃü3¬ð<¯/j¦FYgLÝ0u™²–èÙ‚¦+ôCW5æŒsÒ°sjrS4eßΦlú⛊j£M€O5B±¨H´ñ®Ë’Œ%‚ ˜ -½ÂÆTÑ>ü-*¸Ï›• -¡=‘Í5ó€¨rÀìkŠv6A5rÛ£íHM£ø -nƒ’H§$C1°eCeAe
¢3_jt¸µ_Y”é¼AD€ã›ªi7òWæð€Eg(¼%ÌDeø?"#B¦Ý’„¨“ w²=Ó†#b“Mr»@#D!ƒcÎй¿µ¿9‘f¦ÑÓœ*À´‘kV 1àT²ÁˆZUê6æ¸åÉ Fp &‚§ÙíéŠ&ª"©9H*ØŒ$á”b{6¥Hƒ¬=•& -“=1 -†H<¨P£ãÏð’gc-¼³‰#6¦!¥1*&yeÁÐ%¢™¯{}>›iH6þLÁ•¸:²kœA±—‚+{…®t$¯i$Ñ–r’kV¯"ÿª0)Ûä¡¡¤ g—#§L"ZƒhßÁpeÊÕ«@›2£^Ñ…fÄÃ5VJCA¢qp¯¢èÿ¸:u‡5Êlâ -KK§1
J‘çx8¸†ðQw’gv‰T–‰Í4eQ5)ƒbŠcÜ2H/"Ó0šŽ…±„ž%ýfíj¸ŒE9†d7«I2£]¨YCà7IM¤É¶%lËZ.;¼’‡ÃüdPªhº4Ø5ÉEhžŽÄÁâÝ9G±ÃiJìŸ.$Q’ÛŽfs$Ó·Ð6C->Ó« -xLÆû1ûïÖÓÒ½¯5øIK;íüŸû|üü!† Nd‡ŽP’T¶25M#Þ -vŠ)Òâ#@d¢ŽgÖüÁy,…„¤æÈrÍT·aÛ,NZ¿*#DUîÿ¹†ŠìEµ¹´¦Ê:I´v’Ž uâT -'&IT¹_8
F\RÂ#¹L{¹fF€î\—0ðƒ·¨3qGŠn³Á&¢:¥ÓU涨áüŸÓ… G.ÿÚ& ^K–Ì!ÑhºƖÁ¶4È2Aú•\« óF‘-'©LâÚ?ð{ÎÑûÿÚ6) -N,ük¯K«àK4kÍ6ÝÍ^ËYeÛcüRvôeÉÑÚ€}ºí uAJ—ºº-Ó%Ö¢M*L"¬; -ÑÔédÖ©C§oêmtOGv€Åº¢]–@àó;æUÇÑ©n/% VºJT%éˆ,ì^Ó5®‘1YÉIËkñІéÒ—È;Ž;'Û¯äzš¼.¨™£sVÑÛ]•©vº^_›8päeø”ßÄ“ÚâW†<f»N³Cö*÷g½Ð¬a3ÿËÌÒn³²oiתµàO<ßyµÖœ•@‚(†€Q>.ærX¥W´ù ? <· Ùü½á}ÎXå¾÷ç-ïS$úïùvÉ[–n¶yQ¶]À:äx‚÷Ý.ìgÉ-8Ëú<¯rÝ|ÛybH -üÏñÃðwbaÝS¬Tk¥¶ÕàuÈÖS1߅ν.;¤¯V†Ç²ÖÉãÖ.‰¿yb§R?ÄøÍû`.-4:Å^§Û¬¯©ìYú{§ÁÃðtЋµß¦Ðaç³Ç°2
9~#ÔšÅW«ô>žÍ†õùPdC?b=æÛ°h -ñGÉ5{í¢ÇžO9¦~ÿ|İjÊv\×'!ó?G -»wGb*ŸlŒÇïÕE‚Nú±>×`8ÜêXüÀÅäávßç‘
øð -M°4ëhÈfÛU0ßœsþç“mx -ËCfAYÌNÚYó”Q2 -Õ¥ãf§ŠÒ[‘}b‚/¬ Ó'9øb\é8v:€Á½Ž
-wüy´Ã›)ŠûÎ<êº)útÃ=pPß§½šÕî_|íNkýÍ'ÚÍV¬måÙ6.TUV90}y£EòM‹¾¶m,Ènæ7¬˜íu[½®ï4ßéZíê_ç;µ:ÍZÏñ†Á«}ßZÊ;_}g£¯ -µò]ËWÀ@Gâ>¶òklœ& -Ðø¡»|LQ
}œü¡¾wFKæ‘Í:<o<a"èr<¶©Ñ>t‹cVW±Vm‚€ÛT€6ó|”ëSº«Kô}Ñ&)ü›…IŽ} ¸LFñh\ãCð¤—G:ñeXžËIàØ‹½C‡M9$Ò1Ízw©spÁcpØ{RS4sWT"BDðè´ Î¤{µšÕ^À·€)x;y8-Ûžlþfµ[¸mnïL
zúŒôœUÛÍw¡³L€BU¹ã±vÆÔÌ[í½¤·¦÷õîªb¯“5ÔÒ -UäT|€c -kv^«-PD¯“«Ù@NòQ= tDlj]Ķç«~¶³ß,ìÍáa:ºÞOxP×m!ê;>O\ÎäéXºõ\Páóä•ú 5ýq2ýxØØsì¥Q¸µk²üjÃÕ‡%d_Ý3‰;¶U<íñ TDœó® °³J¯^h䫵 ‰èÇoÎ;M֙Ǭ˜„¾âÎ<,ïæð¹÷Û§ƒ«qÇö•Ô÷°€[Ã{H£† -0·ZÖd<*XÑö¾íY»ZǘªIªl?¡8:—8ÒïfWv}îärï,rñ6æ1‘T -‚·×H’¹é6ÃzkÀìtͲÛ}µãë5^1!ad:ÜBÓÅvµ5à&µ¸§ŒWéÏtø#°i zÐô~o¶_ãÛУß
ö<EÔãäÓÏFÀ<y©}DBZs]ÐHñõ´ëjü.¹$òá{hðÆ -Íߦ`@v(r¬ØnòÝLþOË–FÊT|k$V&ƒÛ?ƒÐ~ŒvýbÒ §¯ß}|n@;ÀHÒÇ]~ô±1œºN‡1C¼¡‚<ƒ{%àUÕrÕV¢•‰Ë - %a¡ósP¬Ü?œ>ÆeôFå$µá#òß^õù”bB,‘o±³U[®Œ–3“·©úÔÃ>¾ÿ޶‡+7Öþ®Uò>ù -mz¤±˜ -5øŠ]ºÇ§¹ôRÓþÒ.õP´|6ðUiÛ¿àŸ]¸_óÏ$üóR¨àè°t6³*Ðþâš°ã_Ú®Ýúƒ©\Ó¿|°ñ‡Nfãþ•‹‡ø.õâÂAÿêsuÓ/ÔOübG·üò\wÞ/÷Î5¿º°±ï×VæüºøØöÑCÙo¦•]ÿÚ¹ÿÁ¿^,Âù7.uÿÖÖ~Ö¿}±ùì6Ä?¾¶œô'nçý©…ö¼?}üõïZwþ½lkÎîÅü™|àÑ´ -ú´=ÿI!Võçöÿ¹puî¿è½Ìú¯š+)ÿÍãfz¹»:×ý'µ;ÿÓ™ºâ/\œæü¥«Ö¼¿\ÚÊø+Í|×ÿº ìúëk×oþf6²ã+^¾ù»+Òî¬/ß›kÅgâs³K•«óÙPb[˜
wO³Âa~}Vë3YõemÖ8Z\š]_kÜÍn-æ7f£íë·ÙDå47›~8Pf÷î÷j³™»Ýìl¶t¨ÌžÖN³çÝ‹³ÙkÚ¹‹ÕfgsþÇÙBz.¯ïI³Õsh >׺Ÿm{ÐKwñ\››Íµçç”íÊ\°Z¸œç´Ý9Q¾6æÔ94gÔó¹§õò\ôôåf.y´ŸÛÍ é¹ÌåëæÜñ݉2wö²ž»ž.Îݛٹüq¾=g=çês/ÒÁËL`®qš¨ÌubÏó³ÙíòüâJây>ô°S™Ö^æUÿy}Þ¼{|›ß:hùçIqq~WŽ…çC—êüéJgsþ2²¶3·zq<Ÿ_›»/Tæ_Ÿ:½ùVw'ð¯÷6g…«£L Ô[¼ˆGùF@ëÆB£…@\(ç;•“Jàð`3Èé‘hàz¶{x¨½¾J/U-ðòøth•‹õ)¯,,¾–N"õÖ‚¢Ï®/¬‹·Ñjli&°°£],^½6ÎV6nNv -y¡ª,<[êíB#yYè…ƒ—‹ËðbØZ½ZT.î#‹ë'ëw‹ñƒ¦²¸—͕ﶶ/«KŇ^åp±¬]‡ë™½ÇÅîktøßâÖ¦)R6¯–4S[[Ú|1ºK©ôúÕÒ!pÅ¥³Fnaéî._\*í·2Kµ¸ -uã³ÁŸU®&BÇA=“Ù -n?t"Á·d'xy-¯ö—Á§¦ÿ XÝ:ƒ^‚o5C]$ßBË‘…Û¹eí>ù¶¼½«¼.ïÊóÖòÉB3¿|Ý´–µüÝòkõþv¹Û|¼
ƒÏ÷!Éh>†Öwg¡ä“P$k¡Ëëv(?Û „^H¨;{ªÏV–çâ+Rzïhe#ܼ]IuÒÕ•l±3·r},¯ŸäÔJ½ðzžmœ½†ÃPX_DZ£·ËðAÜ_¨wrøéîò0ü9„{Ï7B$¿=Œh…Z$ZiA/‘ƒ‹¥ËÈEfc>’?8ßÔöµÕÙ}yc5|zZX5Jsêj¢·û°š]ëÊ«7çû«@ˆêêÛñCAXZŠmòùÜ«°æÓÂ~÷xN¸xN^ -…YЯØ[ÊÌDñ(¸*næ‚q//ŠçM]b¢)6v/¤À[5&I›‹!i«™ªI‰‡kéj9²#¨Ko™¹E9˜Ì5dM >É õé\>Öãûò½9•_2φ2{™fÊj7R67ôeÿ:4«\-Ïwëz¾£täùŽz]îªæ¥>«î$õ|5T‹ágA}[YÖµååø¶fˆO»Z:¾šÓÎNsZ¡¶TÓÞÌë}ùNÑt3XJƒ<Ûy8¸Ò/¶¤W½Ôn.ëÊ–Ã쥱±»Û2qŸÙZ?2ªÑs.•LÉ:86ã-ó$UÙ0Ÿ¬yXïÆÆÊÚrõúdm-=ëžµ½³vmUZkÕ³ÍäL`}þ Ö\W;éõdr±³~–¼Ï¬—.÷×»yárCXšS7¢‰—êÆq¥¸·‘7î‚oå›§Íðöubsk¹Ü<ÊWŠ›ó›}UÞZÙ9hom&÷[GË{[7‡0û[jka;N¼noµï·³Ìñv>½’Ün·òft5»³éòb4·ðGKõ×VÌÿpÿ“ó·ÕXªtý»|¾,Ç^ó•øâÆëKÜ<éÕã-¡ˆïÌÅ[íbh&ˆ-ˉ˜”ÙJäºÝýD¹°s‘œ¿ê•’zö¨“ÜÏF"ÉûËJ,Ùz:Ì¥V¶ª©¸±Lßùc©êrý:½xùÒI¯¯VÌô‘õzž.d»ÿZpsG
;{Á#èeç>P<ÜyÛ»‚yØM·_w¯*©Ýúríyoåì`k/¾ìß{/æN{?Ø“Žö·¬¹Ð~î¬r¿_¹°¶–²·ƒÍ›‡‹0"Mà—ÕnfÉh_g6¥øL “[Œ-g*·/‡AÑq¸U'ÏΫÒáK|ÝÒŸ+G1-u{t©ÎÕ7î’ÙÈaz=›|¤ìílo%û–z]<–*ù¹ã½í»Þñc÷¡{â¿/÷NŒtsö$k.f'å<]Z=N·åýôÂXÝ>ïçV…óÜÎêi!÷
¶Îü³w¡3ãl{ãìxm){öÜ-Η‹sçñÛƒµó›\ôä¼}¶þz¡>lЇ•ËòBêå2Ï)3ËØsùüòÆÌ^¶Ë[©+m÷þå*ZZ¿z~=,^‡žæõëäýyáúþV2nüùjùf’Ù¾9[YkÞÔSÁÌXî,ßÈÏ·¥‡ÂÖ]p³è¿Kª7ww…ÖÖ½ÿ>²0¸_?Z+ÜŸŸä2÷ͳšþ äµ¹‡¬ÿ¦üPÕ—Ï#çç©Ç½%Q,^UBOAe¯÷”hˆ¯O·Ýb~~¯p—ߎ^\æo¢Ç¹|oïø¤°~z|\¸(]ÚË…Ó¢qÔ½˜ sñ¶Ø<ØË—´•ç—ÒIQh—ê·AKÙTëxó&nÕë§ey÷XÎ^^õʯå¨ü,KòÞs6·ðøüÚiú+òIc½r¼Z¿¨Ô^Ûíªr\¯žœ¯ÝVGÐ>_´ãâÞKî.Ti=ïm¾šµÒëE2¦¿v^ZùÚFêĨ]÷D«>{UߪG7rõúƒ”Þo,.™‹Ô¢x×(® -›Í=ÒmîïÊ—ÍJak£% Ù¹Öqîñ©Õù÷goÆÓ–úv¹—ŸmûåˆÕŽFî/ÚËúN'(›ÿc˱zG˜´IJkùž½ªlgnC—›‹ëù«•}y!ŽGÛ;õÊÚsÿŸö¯,-&ªùHg^;ßMiskÑóCe{-s·xm÷Šz:%°¯ç¡“|I>¯óÑõ‡Èrtc¥Õ‰v¤Õ™@t=ãoÛ•ö»ñçÝ“LtC±r`Ml“‘ÈâóPW™Ò5ô§'Ó5ýf§›|¹+7á0G2Ø^®[ mis½tR™¿Œ¿Ô/gɲ°_ÙØ¼n–õ‹“Û»ØY"r1¾So½µûèÆkú>ºÖ‰ÔCÉ•@/½´S*ÏYé§Çl/Y¾¿ÔѹZ+Ç+ÝDE¿ûÐñ´,Š™·èh*¬ -¯Xñš~\g#¸Î¢‰½¹ËÔŠ©‚”ܻտ¶ôDó!´qQº]“ -s÷ÔìV# -ç'æqàê<–MÄ“å\õ-úv¿ö¶r}öÈy£•nRbè"®\ŲéäËåc¢ú¢®—ëçt¢¼,·žtý´Ôtû3roõƒX6³|NK76¢íÃìw[‘“ÐöEþ
hK3òÑõ³îlìl¿ÛÚ -¯ÌQSb¾Z‰™@ìì°³e&ã™ë×QÐRMO½k½‹&% ÒŽr8L9Ýãt𸶞Nª7Rhkç)Öž3.U±œN%U=®…³Äp"âîÅ -uš\}j-Ç_º¥z¼Ö¸hÆÎ*WóÐÄAÈi •ZiIé…°~;-Wá³û…¸ÊT·¢ÇMh›ÆSW»6‡îÏätôyó -üS² T.cçb§¿ÞBìôì¶f¼ÔÂÄÑ\A -À€½°aR‹(µ¬Œ7F£¢^ðëA9 všÙà¸öh,·Ûž¯÷»Ù
¨t¾
ÿÜÆ¼¨¤î—¢67N<(`ø¼Y<²[yˆ¹˜…^ú'jôÔ~t"¦ÁéEòïØÈ@yõÛ-O}è`õÜGF/D9Øó0cÆ2Í”ÐØ/»Eþ¶ïÉçeaL ‹Æ;*—T†‘E/°—QÈbc%LØèðŒ+D*цgÌÆG
ßðþœ&ú¡ñ,R6ÜÛ¾N±=ÖéÕw¶úg•á[dÕ£– -_ø. -Ó„k}…†qhÌdî³}^]ŨEÚ»|ί\?ºÚ“ëâÉmêßeY!mÛS»wɃ¸¶ñ“èírü$pß%uXØßõxòÚ"Dö[ÚA$¹÷¢ç]´eˆãè5võ‹Zæ,–=? -6ˉj§ª¡W!|úCê-1ÐâV¾§Ù?Hn{õÊâNâ FUÖDÃk?x}Ü\‹%6›G¥d©n®ØÄ·4vú¶LìÎ1anú·"ÌÀ¼g—›êšõ´`q<í¯Æk‘SÍ5Hí˜ °ÎûÔãCà€öGßâKŽÄª8t\W› -(›«É½ÛÌ2*§T¸óFnðÆÙÂ|.ý8·p,¾â5c~%¹¿\"åÆYû¤I<^&ÌÙõò|®2¢S„èi'ZÖÊ^ñ†sÕzŒ?D/æ»]§çLñ³_ëfÒ`ñÅÕ³H¸ïÕ¢_Ø»ÝÙàM8ò•<´‡/åòZuñäTº•X:y½»<jh}õÔÐL`ý¾y{7©ÒŲ~T\ÏTY·‚¯äG[;z^¯¦Vš7"pFUŒåÔôýN½»¼ˆõ^l3DEs%‡¦ÈlkW;‰£ÄÅö -{K&Û}çê×ëòω}uë×Ü´wNkEÊ:¹yu=¶¶<jYÙ=K Z…;3²Ü-Úz;ÞúSGëûñUïNQG™î¿ˆ<Ö -M–«jÏW;&ÚÛc@ËiQÈÇŽÆ×Ôå·J»éÕŽƒ¼Åç¤_´GÂo@më×¢ÎÎÕ£œÅàëÈ¢AXW¼D:Ú^o«Ø\[«šœ˜Ÿ¬©nnïnߘ]Ë‘wX9-
#å}}
9‘‡Åñÿ™h–ó;[êç'D³³zÄãõïŠZ~fSäÜS»£[•'c*áå©–÷TNG_o¹(ö¹m»Fjø©ýðLõøâÑv–Küf{cÇéʪœ†â£¹3UM{Eâ.¨h/ÐX
•ÆHý©šÂuR¨;ÝTç{[ªs:´wĆ̷Wí4ˆÖønÞBI´s³nmmu¼Î¿\4ܶUâïšš™Úçå°-Ú›ü•Á…•£ÀÒüцöÞ¸voT²]<^¡ýÉöÊùkC/‡—•œ'~¢°u{åôA¤n‹Yâ‚ÿœ–uïXÄ,vR4^ŽÄœv‰jmä(úi°ŒlŸ¨»ÚÍ–³ýbÏό鲔Gyé¤HQ»ÞƒVoÃà®®OT;Xm
ãEgÊVЧûÚZkŠ{äòòψ}Ñ®ÕHƒÈìȇON¨…×)Sm«C}Ýòqééiyü1›4’#’Ãúlýòɳâó¬+ÐY»s¦sajcBT8%Ó¿¾T514{6TÃôæL—pÖït®V¹+ÛÜî3ÕÛ=#y'ß="òþ@ @缙ΒéšÓm'ËKtgÿÌBñéÖñŽƒ¢ª½¾åæFqo\(n®|±ë‹,¥¨Q\/+u›óg¦su-²í¶^äœsf‹(ißhY+éèÙj?—ò½þ‘¹Ü@a_[8/Ã3Å;ºý鯊ÔgäýgeçÊxge´fŒYóŒl8t¯iÖá’™ZOEÞ#—{×bû\ê:——ηn7g– -çýòb˜ëèÍ>ån^xÙ‹õ=CÝ£UuS+b_Ö¡&¢(j¡Ú¢×{Zœ}ÑÌs‹Ìéûê«êrçm½ZTY»èéðÔŠ«ÎS,®¡ñƒÎÕ¦n±¨‰üݼډÎÙ¼ÚÓ'Å=²Ø²nÙ/_ºÐ:¾±z(·1æå7ý˜´è{oõ‰ãÐê#ó…ÇN[-ާ³–_*þ,Kü§ÿ‰á;tž¦Ê¥jOóäJ™·Ì3PìiÞ8,—óW4ŒÔ•G>‰üMû ¾¼yü°M´º6»sG—eS¾)ò©ßÓ8Zµ‘í.ïnÌ.)+wðÙž¦Í†l÷àLmvñÆñÑÒ©ÒlÏQÝXvñÀTGv‰wÀï-kœ.ÔV_™Ýî©8ðˆDTÙ±YÑ<´ÔTÞVS^#ŠÝ¹K+ÑO½Ý‹ëíY®ýý¦Æ•ÖâÝÁÞ–¾Úƒ¦šî†“¥™ŠÉÎý¹oÇL`z<ÐØÚ¸ê-†íÐZÊóF<í%E£b}ýUf»Ü¡ÒòþÚ<qÄ~©ÿ`¿þ`\¬¤¸Ç[V1Üè–Ô'ý;›ùÞµ|Ÿ¶æ¡èb÷ç½
bÙ5Gž¦®ì|mǵóÒ±YZvPÓUQ³WVü³kKü×Ó±+ÛŸ˜1^iWõBe]ÏB©áJòEû:nµÑ•Véër¯´!»pÿÀçÚ7^é°o®"Ç_W]i–+ºÚƒ‚â“•Vn.Lw¯´Â=çiðŸ6ÞÓœÀBE–+wbw«ßh_½¶¾“•Våæmï»MV:½à
œ‹®T{ç4²Ú®ì†|_ïâ¸áJ»ºý㦇×?Ó4¾¤T”Å•ÎØszR”ä¹£¦~¹ZwâY-œ.ŸÝò¹ÅJ+vŠR¿·-´Òá‚‚¸•VVž[Ü®4Z’ƒ«]ÜŸßØ7YiËrUu Ïg¸Ò…æ¥Q³•vg¹òÊëçŒ÷µ!{þ wc}Ôx¥#í¼sýF+õÔoÕ5EW*ÎKlQ*jži2^iÅô´7Pë4\iNàtuÞè9ÿÑJÅ]o`~>`²¯U¹®Íív³•.{»Š—&WÚåm-X/¬žÖVšåŠ?À‡¹ÅÍ¡•N—ÆàæÁŠÆÐáíœÛĬt¦ÁÛ_]â“+-Š[i–ë §{}§jt¹Ä/V[½_~ûgg–MVZ•[½¹X0[i‡w h¾V[©,cqûÚ³W×rvpÔp¥cõå¦+í[+÷TÖü³%Þ±EwŽÑ¾äôu¯Îθ
W:Y°}Út¥c#K+ÚJ³\‰û:ÛåìÛm0^i¹k"ÐÒÐh¼ÒÝÞ£•Š:Y®vr±+çÐä -µÖ—«b7ª{"a§ÎôÓéß¹I³O7¼ÝK®£è§ 5¿¨z;òMþkqò=íáí:»^÷iUoq<ôéÁfMÂUYµ½‘3eôy°Rî©›3ý´Æ_4ºdþéÆòBAäˆ%~ÞêÏŸY5ý´ß³Ýì7ÿteèT}ôÓ¸#V•;”³ºÒmò_×tçw7L?=U°W÷ßNx΄§§|9u Glb¥wlÛèó`-×áÙÜ7ýô¤k¥,ÛüÓ¹ŽºÆð3ø|ɵPgúéÙÃáÝnÓO7'ým#ÑOŽØ¹Í@Ó‚Ù-6i¤¾ÒôÓ^EÓ”ùsn¯Œõ›ý×ùÙù=sE¦Ÿv¶¬¬›~ÚëoÎñ™±Ölž»ÎäÓÊnogcQxŸë -ëã>õŒ4‡>m/mˆ¿*»Ç—ºŠZ£ŸWTÆÞµ{× -fÛCõÏ’kK~ºº
,V«ž¶ƒÁàßbê1ÿaž¼ÃlÏ.éw˜gÇå¥ògìâŽÑvùÇIíþ-r÷¬#Bë[m×|û9þ‚Æá’P}.îsbÚcM¹åâ?ì=§]òNGwE”
ÔlˆûØ©#Qµæä‹õjˆ¬/·ìLãŠ[ÔN9ûGK¥%1Õí~N–+ºZíNÇd¥U¹ò>gÖx¥Ó3¦+Iä¬/®=¦ßWíNÇt¥2åštM¿ÒŠ1q¬[mÍ`Û˜n¥kùù¹Ñ•jÿÈJËã¯lýGö´k+f¥îiíîU¿Ú˜Üè7]©Öú7Y©¸ÿ…èJžÄìë¼éJÅ>¨0_©lý›®4Ë%ÛÿÆûÚ]jµÒþÓ•jmŠèJåµ³ZÙ¦9«ëµáÕkwûÌÚ9;¿·ptn[í›üfUîÞBçÔò÷*7Bå.T[´‰}žk
Õ– —îRá9ytFu4M£ýPå’?ÖwÅË'=ÅÑ?švׯC7ë²N -Þï7íºv#K,ÔÑTÛwVÛŠ¦Ú®ÙQq-wÊ5—G«¦Ðê›:]¡?ŠvB+КÀáDÚ–b{ÄÎ5î)®÷IT{meë®È£ús¨Ïl(òËr-]úÝ]êmõ)‰Mž?È—
Ú£¢ðÑ ¶ÛCÕ±n†óc`¤ÇMl°··ÒåÒþòdì]’ÑFmÖöÄm”~“ -DÑ(ö4{uýhqÝÚ!µåüQ[Ÿê Ë?B¼™7Ø¿‚lÓýËrE÷PûÃô†ÏßXŽúüåFö¯(TÆŒöP6^',–ý󷣕Ïà…̓e±0_QÛـ颲ì÷ðÁª/IªdEËULF–¬®¹½ä¼áq›™PINûÈûÛfj{{–ùÁŠz܉UÏBglÕSnZõd©Šnç\Ͼþ -lò,{=¦ îψoÉGûúÎöȇz'3Q¨:}ó]Ñš!æ]8Ó{rQö7Êí–ÏÐ+«ƒeÿ7½íÑî’ÄÁJò9X‘K\û/Êój7Ëb/ð^¹/±—¸á]·ºwI”Ú{=$V} ½ñ]²ñ[cÕ%×Cr®7¶orS¬î’»¦»Bg?én’^ù^€ÝîÈ]Rü¦ôŦåº7D9Øók;¤½©’p¶ìŸ«¸œjÒï“å²ê¥;×+îN§Õï#‹º¯/®*0鯑G§ÊVIÖÕ›1=
»1Mé–£ø¦´(扡:ÙFcZå¾ø¦t2Ç.ØÓ¶Çe½=vûÊ›'ªâJ„î^,©DØ×Níz©q[œ÷E“_$¿¤Ð½ÛŸ÷Ìv(ËeQÒEÙ‰Mz©T -í¬Y§¼,Ûˉ½‘LnkBÏăËI÷ -.%r7]J¸NNf9Éç½Èý¾ÁÂbž&—DcŸh‹âÂØg»öšjFž0´k/Ÿf¹Ô—¡t#æ/MúŸI#B¾ËYdëPꚟu{1Y,¾F;Øt'Ôh›ñðlÔh†=$íâÀ´ì¥[£MDk´ðÝk2-øø…©k´,›OäÂR¯Ñ"We×äNj4qÖŒj´¤¯}±œäk´„¾‹àrÒ¯ÑäRÒöª-ÇâiPq[aä)NY–˨5“„&w¬î õÛ(Å£±ï[Š{ž„¾qù³tÀ†óË©‚=ójÈfˬ]\çC‡Fs°>¹jV,l2;©J6ËeZͦøöCìÅ<©ÝKÇ܉§Ò@g-î¥&³F½{M{Ó¥hý0b9é¿¡-Åä†:îÉ»z9±Ýmv/<MЖ‰¾ê`.ûç‘‹Øl(~Z’Nû^WH—sFÒ¾ã›:i·7K×waš
O&Õ¾·îƒ•K¿}?·g”“Íbò¬%ߥ•˜ÅärÒißë–É…©g1m9í{£¥Dî^M–c7ZçB†™ÛK;ÆäÂÅøçÄÚZÄO3ò:’¬ßà -åʘ—§¢¯a˜¶÷-N‰þPÛxú65•N_u\?¿XXºW·n»®mëw-Ž˜ænøLʵ˜Ý‰sYjë:·xzÚ.Žçd™EÏ•×éâ6É´Dhïö(/¯øÄÔ^Z’–ÚKËlÝWÚHLþ¶©5Ó×óÌß\3¹ß[æ·w(m\\+™{ÛV,ÌôÙM¤·Çîõbaåyõ}ý™è!G¬:¹&†É;ˆòL.–Úêƒ
.ÌäbŸ¶|ù6X"²Âý÷¶6Êf‰H¸¸B9xéFóYSåÀ¶áEèh£åNg—l,ÊÙw;³KZË丹N£tÁonI>‹%Ž ‹1”ò:ëtZœ1t¦+ÕFЙŽLrõ:ýhÁtÆÐY ‹iõ¥1†Îz]ÌhÁ4ÆÐY ³-˜Ô:ëtÚhÁŒ¡³þ½ÐØê´ÇÐ%\¸1#è"wiŽ¡³A§µÇÔcèb^H¶a6\0jm›¿o>(îe›dØÓÛ¡~q»ÈÞx©@Ü=rÊ/ÃvÄ·~
:ìöôÎvÄfûäNî_Gü)§ú¢˜â;V4’ãí[?»>©êD‰}Ægµ0ë÷°ìíŸÖ×§9g{ÿâz®¯û½,ùM2ù¨è¹²Ú$£!sr_’4g«®éŒ{$únOr¨“z$øþ˜áË Éw›ukß–vÕtI®õ®eÙìû&d²ƒZIç<Ýãà`7³Ç6±wIªÁnæ÷§Ötµe§åË 1·¦ªÛθÑ1#…í-,ö,_â›ÿžj䣽:k9ÔM¯vUšÝö®”›¿B™ÔSmìÛ€ÍQ‡ê{{q°ŒžeGÚc‰]gªŽÃ͚ݸnñßN¾4›Â«.Óf—é(0]K)~˜²Ùe{ß‘êø$Æñ¨Æ¿äÆtZ™M3cÝ”N|¾ q#×l£ZŒ†hšŸ?å;ð¶ß"XëŠíÁ4<vÇñÕì«ÆÄX†Ø·ÓåÂ26Bt?úäÃldJ2S—xûG,¶ƒ5Í#¦&j'£5ZJG,¶ã×ß65]û¢SwÜ‹õ#¹ìµeú|o³"—ú¨q¿¥õx¹¸E˜,`3'6#,bÉ¿à“,Y^ÝÝ6nö"w|Š›½ÓݶoöÌÿŽb -‹P}5Š8Ï‘oÕ°7T.¥3´èUç—p“Æø²CÜÔY–]ÒrTšÉM\B±·#—¥ælã‘§¶CÖ_y’•p{eÑŽ¬;Hx•¿°ãÐÆ!×=1+Ég{’ëÞ1¸U -¶”äjóQ‡†Ù<…öXÝ+>z½›¶ÇÎö$×½c>Ì.þ
¢”Ó†¢§!+îV¼$Ù#³I1ã‘ÅFù“éþ°Ø¤Ø‹YÅ,ŽS…ÝM²~;]=(Îj“â¿»£4®ÿ78$)=2½ZLšo©‰–pž™Ð3>Å𜼺Òützdt÷û½é÷ȈZ¼Ý‘Ä0´TzdF¦ß##‡¡ÅõȘHUÏ«HªGưŸ¿×Æð{ƒsä=qÁ©K²½×÷äTÞØÆr_´±lþÞ¸Æryó„'×Fa°“¸Û—™7äy©)ÈL§ŽØµºBÅ;½v†¡¥ø»>¿Èdæ½KIKxuÁp´ j\]Ò/Í&¾¡ª«³ù¾žå¸:ÝÐQ‹‘ÂêNÐ>Ë/©Ò]½º¶¥å«zÉßE*f3хǼ›½˜”™ñpr-™x§Îz<\òß?–Êx8£· ‡Ïez<\ã,’gõ†jæÆÃÉ^ë4¯@ãá¾ÔtWêãábÞ† -ÿ7™ÿ½£Áq™§ü–€ŒŒ‡‹ybeãUËÔÆÃÅß‹™=Ö‘#ÙÒY/\ÆÞ‰‹Š}'Ò¬miçÈÉ[ïDª®ýƒÍòt›íÚéF×–S¬:û6—cg‰éR‚í1m9É•ð&ƒÅ;ðêM®3ïK|ßY7—ÉÏ{E o<ì)NDÜEhÚÛ35™Îe|¤ëUH÷2³üZ£zÌô2œÞµ{Z´ÆåñNî~Ѱ eæÛ€µå$wù˜ŒGËÉÈ@ÈŽ2{YL¹ó¯6ù6-³×¬¿Xùmh±½ÒqßÖè.NìÃ’ƒÏ¬¿ØIyC‘êËIõÝ>›}±pJ#R—sm"µ9"u9gÅN—b¨_FF¤ÎíefDª\N&F¤ÊñbéH•KÉĈT¹{_wKlô^Ÿv˜ijÒ/¹KBg?ö2œJû2Œ -g\ez(œö>¿å=™ -gx^2>.õ~ËØ#f}ïžÄ}eCáôßA¤
†;/CázÎÃP8ãþ1›÷gÓV
C}U ÿþd«O¦ïªÚùFøØöX{©â¶Èî‹^rQ•¶zmt Ë…™³]’m˜„/Nöá‚n4ú´ÍG8VßÎ+ªr£”]‹zpsü¸º9ӯÉOáÞQƒô½~å¬qÅFëmŠË¦¡®ÉåŎܵ£Î@m^ËB`¼i¸³¥ì°@ÔüñæêimÚðŽ™Îý–ÖîªñÞö¶ÒÕöö¶²>9 ÁØn8¹¶b79Ôã;ËhÔ™¼K -Qš5wV3R7¬/^±ƒÝêW‡ôϺ•Vn¹²wÌFØULOY
v[+3]©70Þf5ÂÎ5tf`Ùl¥K+í*©Õ4~,Vç@×?Øbj£?23aܰœB«Án¾Ò¸•ÆÎÇWÎl„]Unî^ÕÑœÙ`·Y«Qgç¬GØíŒ›®4¿}cÍl¥ëÖóñL™¯´sp¶Ëôðæm—×/št$v„vVÅÚkío¡r^gó÷ê~/Ø«ó›sý¶–Xáþ^(uöW4DÃwIM£ø„jÕwlã•ÛØ¬g;¸/1ψ†ów23´Gý†j|¯‰ù<V{66I÷\Ìb£T/§ªÞóÞ½fr&9£iG¾SÅFï’ÕLrIõÂÍvØ~ER1RûNÈ$ÞëSM"gù^Ÿýò¤˜DÎtÿŒæ}SM6bwÿÔs
Ø>èªyFâ¾K-ùãì_/]ëæ™¾îjw:ëžÞTFÓ¥Ö“ìh:£û€ð\œ™MgóûaÒMgÔ'½¤?šÎh,]ª#ÍGÓõÅ¿iŸÎhº˜Ãºf
¿,ÑtF‹R|CH -£éRÌÈIަ3zNÉ•Mg4–N¿Ÿ™ÑtFcél¾Û“Äh:£¾öðì™MgtvµžÞŒŽ¦3jÜèßÍÌh:£±t&ß6ŸÆhºÄMÚÈ5o)¥:šÎ¨qšåÊôh:£ógð6Tš£é⥜S8¥ÑtfmËÌŽ¦³ÄÒM·¨øgâM—ÒKz4嫌¦3[éÑtFkÉðh:£§%qïÀg`4Qõ{÷š‰ÑtŠ'#M§Î/™Mgt0¢ñL¦SäÊÌh:£±t¦óñ¥X½Œ™_,©¯n2ŸÅ+q’Êø¹9l|ÚS\ì1¿bÇl.›Ôç«KؤCù”'ÓóÕ™·.ì§COARÇ)z”bÞ„”c2•
›E f€‚ɨg³ŠÛ$»UÙåbL¥´Iòˆ‰JjÚe«M2~‘´†±8NÓ.›T™2‹ÅÞ5ûzâîˆÊójw›[÷ÌEf˲šè.íiîtßse³IžÊ4w¦³ËõÙ«dsš;³‘\öÒÙzAÂúýäèDwé«®é];³ÌXöRÙšæNÙ£(LÚÓÜ…Ú0ÖÝ¥=Í]pì›b¢;{vû2ð=WåÍ>ë“hœE_Æ^QÚí‹6¹SË#vÑm·|š>å‘Ô)ß=T•O9ŒNÞ<[öÀÛHgçæ,õCó—8l/k±xí0‰—ÈäÑ1íú‹-ÉöfØkÎ-ˆO$¿4ÕtŸ¾ÊìyO×ð¹vż
% -•â}pÛoC‰E)ß³ý6”X˜½7º_lÖ꾌Œ|LÿQˆœQÐâëÀCgßærlÍíj´Ý›]c™¸µ¥$Œd7)¬Ë®-ÌÖÀZ³9 âÖŽm'¬ÛÎÜ·Ê…ed"cŒ‰µS™és—é¡7:”Êù^Þ¡.J|¤+Gâ%¾Amy'nöõúÁfFF¦D¾ù8ù–Dâ¢ú25ƒ¡ø`Øü±ík"©‰ªÍž¾É‰×ÒnN—bõVgËÉÀ·—“îlÕÁ¥„/B£o8Lî]Õ ’zÂëö\†“62ØêO{†»ðøJ³9îR¸
^®HuÞêäf¸³ù˜Æe.h¡î22Sžr<‘½™òÒO™)/ýËÐb†»ØqIvƇ¤2ÃÕ|¯rŽ»äg¸³ûærôSúkçö¢Í³»WÛkÅÂÔÍœ,;
àÜt©
¬ùX™nÿ‘_8`8:¹ï¹Ò–“B½™ðr9Ï9àµw/¦\ŽùXظALYª¯™Ð¦ËKf|»Ñ &ýSQYA'\†u…Ša£_+<ï›ÍaLv14.Ä߉겘aL±;©è;.*r^L‡1ÕÚ¡nyÛ¹*ë -í\•v14nÇ7¾S½¯œJj“ÙDrîD‹L›\Ãp*4†7oåk|YâÔtpˆLŒqm/5ÿV1yÿ’ìŒtIM÷¨JóöØt澚jZ{ú–¡1®Ó¦_M•|få ©é3å•¥8ÆUw}F^ð1êY=ÆUl’íQÏVc\C=@òDôW¯/Üô“#ÞºÇ;6}íme½';r×{Dz\Íž±ñÆ%O•ø[×°6º/05Xó4åtÖµ«ë;ý-fÞ`ó¨~¥1ãá²\¹“ÓÃú®ªØyØêÚg§MÆÃ¹Íáí-Õùbëä¸axÞ"³±U¹yÕ£îy³Axs¦+û’Ø-7ÝWoWîÌ„éJzVJO›ÍÃæŽ®42’+z€‡+ýº•ÆM;(¨ÎÎ#X7ÊÑ]Ø<»e´Ò,—<ÀñsÎÅÃ[Žû§?¼}
Ã&+ÊuUv,Eû`ãGÄ-Z¬´+¯Ê|¥]#¹3F+Õæ}«éŽZ¿ÒQ«yû&ÍWÚÙ9ˆy³K¬6O~^ù[hæQQcYìÙ7ù½òv¯éïéÇWz—ŠêZm,ÑS·sØM“bŸ§+⑇5¡ÞžØäÙnÑOIt6_›µÒ=
Š™ÅLýâ¤Ü,–Ý?°IÙCb>$)ñÕ*ÓM’%Ù|£ÚímRl:ÈÔÌ,Z‡®bf»=IÖ¯V™žº„™YÄ -Æ’yÍjÔ]Ü7O¦1*Mñ†fð(eÙ+Oq½’Ý¿H¯µw—ô,‹&›”ð–Vüõbû 'õ–V–Ëj£Ìû‰“Û$Q[š¿§Úž„wc㪫ι¦¢Ø‡×‰s¦¤v+!–Ýc㫯}°™úòÆé’l{ßphc4’e÷²Ý§<é|mtLbÂWÆ¥4ØÍø‘uRßk½Ð™bÏrì·hÊQ€é~ ÑwÅ™ŽSݵ$~WœÅ
qÿXìK#X—IÍw`^Ã2v¼ˆ¯aR}êpgvkO¬ÔÓ×ÅlY|?¿ºËN›Ï´W¥b/á‚ÍšýäÛ#&s?4N™?Ln YÂ|iLËf=’)©1VµI}Ïn“âÆ¿¬uYõ+[ã1›¤{U?ô´ZwÕ&9p3ÏÆPNý÷\YTMi¬8Ñ'ïk]ê:¶CܼƆ³Ì$±0Õ,:ñ£k-—UÒ;bʱ<öw2öÉešGL9ŸNRG¬¿bÎda Ã…Z‡¡#–â(@»c -ÐîÀÄþädFÚ¨ÿ†ÃäGÚ¨õZ§< -0¦¨XÜaÆ¿›Ü(@»c -ª0&C–îDŒ·lh½*r<cîDûäØ7ÿÁF¶H+½ÙžÙÁ<oÙÔfuð—äxªì¼¦žRoávvÙêìnEùþFo ik¾vd¢¨°äœ» m¯ÍÛ}z°5wï¨*{ny\üm©ÅUYSß?0´7Z¹·9=›åª®,XL•ö•îgWöwU,µnN4¯
TÜ_¬Ü›,\?•w²Õ]Ñq6g|´o³n«ajg½¹p{o±zϵ߲]5˜}r°¹ ¯¼°«Êuj¶§Ç}´‘;[±3·^ºöµ±Mãc“Ù¥ž…–lÿé½IOCyA‹7ÐÖ׿
œZíõvUåîï/ÕîÔœì9È]¨Z9¨\™ŽŒ´÷4–•œ,-ï¯Í {;[¶0·#'›«Í
ŽN¬CbÆ—ÖÏô/Ö¶®ŽìÊêÈr>É£%Ÿ«¨µ¥¿×è`iCìîa¾</åyÙ¦ûªh,\.XΜoÊÛÞ/äû+Nåne¯É‘¤½áÁ¡ÙGÅ»ƒò}µmmLbvk`|ÜåÍ[_Óî÷‡wb›ì»q—صávÝ ¹h«n×Dµftšª\íGY®Ž™Àú”6fû|×èTÕ`ûZNKÙaCoKÙÁZ} 6ws¸ódKù‚8lÛM][“SsÝUÙ+b9“{áŧÏ,ôy6«åPÆé…–úþì}m¯ZÏíÈ’ÜÚwò䢧srª[þMìððV©´[¼oªœÞ×e‡…òm„ÅÃ`M\VYZþ›¯Pë;G¤z[ü³ºHõ+*œž]ñÏÆb†É-Ylv_ü ½4üÊþ±Øn¯§£Øu(ìîéñlœë\ÝÜ«õ–5U»µ
•7ï+j÷Õ·ŸY.ó‰=º–óÚ³\áÚK¢ùÛf&šÃôDÖ×§__I^ÏFøÀºE•±³éëêÏYŽüÌýå,—¯k¡t5òQ‰n9]»u^ù³²Pû¨rÐ+GóuWõûå?ýÁe¯,¸"Ëñee×'[è#%º>Ø<AS¡|Ýj¤4øK«Uír9#^ùrؾoµ{Hûgh±«3³¾Ð¹šxÊÆ6ËŧãÅÚ§~wUÝZx¥ã¥áµxÝy5^ÿÖ˜«³¸Ö-J‡w2O¿¿Tz,, ýG¡.±Ä²`a7X^ì{ãö—èÕ-ÑW–{Ðà™¨ß¯jš¨lž_+”W]›ß=3–yĦgý‘¿Tž×аXtÏöùÝGk›á]?Y=aþŽîFY¦O–QÇLO&ò<ÇêPeèo[þÐߎfBmK§{y1ô7ÿéJ¹ˆÊà"gZägJåxÿâV¿7ø_/†–½äžŠ”¶¿nk–ê×ÎjhGl¦"rU®èöeédv»Ì4õò5…ѶÒÓEë§ú;úz²Ç¢IäaN{¤s _×ç}’ØVî:êYoÏ|™¶åùþ‰ -mkËóëg+CëX>ù½òÐïMžkßš¡öñù@ÇVÎjëèø\A–«s¥¤åd´æ7¯-C-ôþ:ý£ïØ;_-»Í޲‹7ÎNeO.tfœœÊre»g -³KÊ -DF>ª–¹¹;»ØÝ<ží>³Õ”]²=2—íY-•é(/²Øü`WN°:jœ.ÔöÔ×9×Z(k¬¢ðu<¶î…+[Þ+ WŠ…ÑJ1RÈ7K÷ä%ùísÝní•ýÚ?ÅÓ;¥ÁÚ° %{^;§²á[¬ËöÏ5ôÈ·:§Kƒ§ìL±H]²ISžWßÙkôB†¸íì^ö‘?äb-Õû 5¨VE‹ÊSÔüºêS«©E½)·¬Ð?°²Ù¶•³ð/TGʆ¨2›ª'»Û—6[×ÄJwÛBµjW[^ÛZ{L”»š`9÷uMÖÛÉ•åyµçµÒ&+Ï’`½¬fwëÊBUfhÙëbõkb±£¾È.”7OdŶۣwoZ~qmE›oÚª:/®A}“s¡RTL£‡²i7óýòg%Ú<¼5GÁ_Ï_éÓ5sµŸeD1œ°ˆŠ¢®ÒŠîÖsÞ5×vÇþ~ãN‘¾%¤M›ÝPþÆ!à/¬.6^ƒíW[^Gx«ù ‹h+ˆ.À˜êòEZÌòˆ”žÓʹ¶–í²3MZkF´&¤‡6¹þÙô~Ù™¡mO´ÒŠ\LCîs…[~óEmvtjgy½³ÃßÄQ›šV{kbP‹ M,wèh©Qæ³ðÛ-'‹–ŵè ç‘–›™ø²¯/øÚ9(¯Ú5JýG¾h{T›Ë[¶êsäÖLÇ~E‡;zĦ²wÊ:ƒÀý¶éÙpû¯r#ËU¸’7:'.•Š®øyC“ ‚þ øODÁŒþÄÍ,®M}'hÁжm¹'f–ñ¥ðA¨(‹¹5›ñsMY‚æºÍmíhßÝQ7tI´‰\i¨%¬’ÐA°:‡á²”QÆ´í©8˜Í·Sšë†C»ÍÝ9Ýó¥9g'‡@«<ôå æ[΂݃E©ÎgR”¡ïÒ&–Å,—ÝÒúÂÔ¶!ÔR -}Y[:»¡un™\RÚ¸WõnLÔ•%6b®ÊâÚÃÈÁL¥LûŠÛ]ÃDŽƒv½Xl…ìP)QíÆrÃ`tå³[¾ývÛ½òŸÕVXJy{áÓpÓÕÝæ‰|ÍMÑì~ù^ÌëjÛs‡~3pÖç1øBœP9_^,H³Œ‰Jaze¬¿¢ÔtÑ2f¹ˆ…oºe¬`£0½2æÝv[/@+c–‹è÷+ˆ~ …t¡º4ZÆR9¾âÓe6ê»0\„¸ãU_®VÛ |žhKi7DëÐøPœÎ`ÏUÂÙè:]¡¼â·a·NW°µ#–ìntçµ”%U*ƒýnt—Òº6dŸƒ/nBGÌöµÑÝ<êWlÃ~S™/²€îŠÃÒ‰}F–]é倕ÓÑ6d¹¬·B¿ˆ]ŸíB·
¡ÚÒ·šW]’Öõ¹ZÒXjz(#µ¥e&Zj·s‰[Õ–«Í=Íú%vµèÿ92ѪÝ,h]zþ¶i_¶»‰ZW¢8bç·3Qû§6{éùìLÔ>÷/çµ3QëJÌrçÎD+QŽ9¯‰ZG8bç·31T’Ïsg¢Ö•¨ë!9?‰¡÷ÇÎsg¢ü BwUžŸÎDí -¼e›9¥egr†óµî)¯·°ô töEu¤Õ0Ó%Úû‘ân“AT³-òŸu‘ãÏÊΑîâÈ‘ÿŤ¼f»Kµ§°r \©öÞ¼Îå{AÝe¦ß×í58Àòƒ¾BGQ6T—"]qEÁ‹TÜ D~VúÙ„v/ÒíQ[“û¢«¸ú½ÞÊ•>yjû|ú -§mO>ò<=$+—¢HG¥Üñ¹Vù3_øajuè8t´C·¿ qtOÖ–]ýeáeËêJn¨®¢õaøy¬8ÿÜ”U]]ë;Q]#þ(=ÚZßÚ?súÌö‰â¬ú¬²ÖŸob{m'°¿¾>¾þ´ÃŽÕ£sëÛ‡'êN”µŽµ÷ôÔTv¬¯î¬ŸÐ^*ª\ªŽÔuÅ¡G¸¡^ãr³îdœµuœªíÚìÎm\î8ånŠïl.Øß“Í¢Á!û]ÙÅ‹k³ÙÅþÀXva÷A±üçt°Ç'²g‰8—D–¨*ÊíXßo;êôôŸŒ»À´¦@íÒxWc–«ódÛúhKÙÁ醲îÖ™¾Ž™ÀäXKÙ¡wGËÅ¡çŠ1OñuOŸ
Vïïûó³Ý
ƒ²w|RvŒ‹zv^GMYv±»¥%Ôg>’3(÷oP~М]ÒZ¾–]<0Õ]´—»/{½kôuù~OèR‹æÐÀâbø4ïh™1+Øu.îMNË+er/”Oäe§UáË®Lû§¼«-ýíìºü~¯î`úÔ÷žkÿ¬+”Ù;Ø…,_òî¯-âÔDÝaøîñF··{ù°6\þ“lhm -?¥Þ£„j¹Àbß}®Z>^*Ô^‘9í•Íwè§Ë{E¡ŸmæxäóåÊHé^‹9ò"½ÛìâufÅ÷)k·*‘'‘÷Óæõ·&ÍzbjZ=+݉{fã)XÌ—†È{½tºÌ‚ßð<c‘C0§=ÀŽ„£Ù¡ÈAˆ›'¡Ø£~ZÞ2RX:ËÍCq¯“Ö˜>¶õ(P›øÞªãQõà'ø%©?.ÐjÚÐ#ÌÐs\£¢d¹í›¡ÕeQ¶a̱œÓåIg¢…AìKŠ‹ˆ<Z¶qI§…ˆ_„¬åËÒ8…Ñ'üZ™–G,\ªm–éBýÙÐ_æÇ!+î yaô`ÚxÈnðˆ½0z¢ÛHÃVý-ØÞ•ÈüÛq¯4[!Á¹6ÆÛF¢Glz>îäD3ÅsS/c"%”ÚÝ“2–æ_¨?)–1}ö‰©zMPÆôW¼i1Õ/"~7ô—køP”1«#‘ÔÙˆ.@÷Š¿ÛÖ‘°z%GÅÇlCl³\„Éoçtï+Ýѳ‘â›Eúdœx(ÃùÅzºšình_A>¡)î†QÅ·
b_,·ÂÆõ™]Ð17Z€ÿ`!vE¡?i‰¢h©´Þ†HFNÜŠ¸L”ü6DKeªµe‘Riý²W‘u¹4ßíöpýìzYèV?<S¼{M¢k0ÅŽAy÷šD×`ŠƒÁ^8Û]ƒ)v†úúìv
¦Ø1(Kr]ƒ)vʳŸD×`ŠƒÑ¯¾²Õ5˜bÇ Ü—$ºSì”ç%‰®Á;³’ëL±c0ríÛëL±cPîK]ƒÁÑI¿çþ*Ÿóûž¿6)HCÏy~Ï_{}!¶â:ïùËÞž‰ÂšÈ“ÃÐcÈ®Óòˆ
–†:àòZÂý]CÞ`ý¤«»&üá'[ÿÜ”U/*ÇŸo±s{Mÿì/Ëå?[?<Ú•¿P¹Ø¶~úÌvÿòÓ×÷³|'‚ÿóŠÿÉ?«kOøü5'ü••â•ò§ý+Y…Úïžð¹Oô‹£³XÖºØqfõðÌÎöòþÓOÔÉM
ôOôtœ¨;üÝEñ»õ' -ÅÖxÅo‹Üòã¢ØÂÅ,ï‰VñSOÍ:Òþ'V[ZÚ€©§‹OzÅ_ÏŠ<õ„Ï{bàÄì¼÷ÄšüFƒ[8³µ^§Û‘ÑåƒC±'…nùßwe•u¬?åÌêúhWÛ‰©Á¬ÙȾÕVœ(©<1¢ÊW~¢²¦FüljÃàcù¯ð¯„ÿ͉ryªµ_®ÿ¿$ª¶öŽåÃå:±¡5¾òòʬ©žÿAAAAqÁDî“O8Âéý&‚ Óp*9’@ ‚ ˆ<σdO‚ âÂÇ“©“ ‚¸0Ãñ¤Fê$‚ .Ìp<y‘: ‚ ˆ6ÏS§OAq…ãYéÂçô)"‚ œÇÓÐEÇé3FA<páxÒ¹8} ‚ ˆóŽg™KŒÓç“ ‚Èp8žY.aNŸ[‚ "áx69>œ>ÕAD*áxú8žœ>íA„Ýp<eÀé"@AX…ãizN‚ ">O
0ãtÑ ‚ d8ž ät!‚8îáx"€}N‚ ˆãŽWþHÓ‡ â…ãu>Òátñ!‚¸ôÃñª™âtQ"‚¸dÃñ™åt"‚¸ÔÂñŠçÓ…‹ â ÇësœoN1‚ ˆ‹;¯Æñ@rº¸A\”áxížÓ…Ž â"Çëm8ÈéÒGq„ãu5.NC‚ ˆ:¯¥qápº0A\ áxýŒÓ¥’ â -Ç«e\°œ.›AD8^ãÂçt!%‚p8¯‡q±pº¨A8Ž×À¸è8]f ‚ èp¼âÅEÊé’KñÀ…ãU..jN—_‚ ˆóŽ×´¸48] ‚ Îc8^ÇâRâtq&‚8/áxíŠK’Óåš "“áx¥ŠK˜Ó¥› "3áxuŠKžÓeœ "Ýp¼"Å1átI'‚H=¯Bq¬8]Þ ‚ R Ç+OCN—z‚ ˆäÂñjÇ–ÓeŸ Ân8^aâ˜sú - ‚P‡ãU%KÆ$âÂÇ+É‹GõB8¼Ap8^=^˜8æ—êÁ'‚H!¯/NŸ -ÎÅ…u:‚ "áx•è8§Ï€i8~d.NŸ‚ ŽW†TÂ6Ãñ#ÆÉ"â8‡ã5!o -áøaä¬q¬Âñjú6ÍpüÀr ‚¸äÃñ -j6ƒáøqæTqI†ãUUëy -Ç<§• ˆK&¯ô¨Q€püDp~ ‚¸¨ÃñꎊôÇO -g™ ˆ‹4¯î¨Bøpüqº ‚¸¸ÂñŠŽšÓÁpüdqÞ ‚¸(Âñ*Ž -óBÇO€ ˆ9¯Ü¨*/¨püR‚¸0ÃñšòÇÏ&… ˆ*¯Ö¨/ØpüœR$‚¸@Âñ -ZñÂÇÏ/eƒ gÃñªŒúðb ÇÏ2%„ ÃñzŒjðâ -ÇÏ8å„ ˆ>¯Ä¨/Æpü¼SZ‚x Ãñꋪï¢ÇË -Š -현ã%‡FD:áxíDUv|ÂñòC#"µp¼j¢;náx)¢¤‘B8^/QƒÃp¼,QØ‚H*¯”¨¾Žm8^¢(rAØÇk$*®cŽ*JA6ÃñêˆZ똇ãE‹²G„p¼.¢¾"/`?‚ ¬ÃñŠˆÊŠ†ãÅŒH„E8^QY‘p¼¤Q ‚0Çk!ª)BŽ—7Ê!A‰áxDEÄ…ãEŽÒHDb8^ÿP;‰áxÁ£@¡Ç+ª&Â,/~”I‚ "áxåC½D˜…ã%’ID$¯y¨‘‹p¼R8 ‚øŸµ.¢F"ôáxQ¤páxµCuD(ÃñÒH%Âñ:‡Šˆ°Ž—IJ)Açp¼Â¡""ì‡ãÅ’RJ\˜A>¯áxUÃy$’
Ç‹%•¸pÂñ“ӓ¸ ÂñrHÉ$.„p¼Øxà]UÄù« BŽu -‘ñL–¯¬Ë.ØÃñÈG>êÑyÌce<.6´Ÿ=æ1~ô£ùˆ‡?ìŠË/5YÒtÍé]#.ˆˆ–0™!eÓÊ×#õ˜Ç>þ O|Ò•O>‘“““›â§Ù'N<ùÊ'=ññ{¬,k²¨‰’&š¨Ñ(gD(B%LVa¡vÅÃ!Ëׯ<‘“—_à.ò—”––EiiIqq‘»0ß•›sâÊ'=A5YÒ.Ë -4Q¡QÎŽ}+1Y‡Éö‡ŠL°Ç=A”/WAQq©×_QUS[WßÐØØ”
õu5Õ•~oiqQ¡+7ûÉ¢¤=æQx˜V£QΈH%&K˜V…=ü‘²€eçæ»‹K}ÕuÍmîž¾þÁÁ¡Äèïëíîêlomj¨«®ô{KdI;q¥(h|ø—gÅ•3§÷—xÀ#Z‰‰:,ë2Q…=öñO:!XYyUmcK{WïÀðØÄÉ™Ùù…Å¥åå啨?YZ\˜Ÿ›™šêïíêhmª¯©ô—»ósO<éñ•Z¸œÉöÅì¸E°ˆ+±‡^v…(a¢ -Ë+ô”ù«êšÚ=#Ss‹+ëgÏmïî‰xJ4ä?öwwÎmž9½¶¼87=1:Ô×ÝÑÚX[å/󺲯|Âc/gdÍã‘"&+±ËE–|ü•Ù.w±·²¶±µ³wpôäìâêéÍý§<ý™ÿü/Ï}Þó_ð½81^ôÂ<ÿyÿû9Ïþ§g<õhgscmy~zbd §S´J_‰;?çÉOx\°œ=è©ÎŽW„‹X¨{ôãžx"¯°ÄWY×ÜÑ=0:9»´vf{ÿ)Ïøçç¾àÅ/{Å«^óÚ×_õ†7¾éMozslˆŸ¼ñ
W½þµ¯yÕ¿½ü¥/zþsŸý̧înm¬-ÎNŽtw4×WùK‹d9yóŠËª¯Îœ> -Úïwß½¿ºûç?ù±(gßüê—>û‰½çmW½òÅÏyúîéÅÉÁ@Sµ×{åãõ°¬‡ÈÊìï¨Ì.Wc¢Áÿ‘)ÝeÕÍ]ÃÓ+›‡¢ˆ½þmïùȧ¾ð•¾ýýÛîuؽ÷Ýÿû?þI+`ÿ-`áêêo±™RûÑßJšVÐDêüM°œ}ïÛ7|å×^ó¾ÿxã«_ú\QÌ&:*K²Ÿø˜G\þÐk7 -6¼D„üÚ?þj¯Å¤ÔP&2§VÎî¼ý–›oüÚ—>ó±¼ãM¯ÅlçÔüx_[]y±È™÷™áN3§‘F„o*µj,·ÈWÛÚ;6zï™Ï{ùëÞöÞk>ýE‘'oýñÏîúÕo~*a¡Š)X{iM-Q3i·÷߯ïÀÿü}ðÎ3|s)jVh²œÝûË»~vÇß¿é×î|ç›^ó’ç<u{mv¤»¥FäÌ'=öWP™]ôJ•â¦R´ÆD5VÙž^Ý~Ús_öÚ·¾G±›DžüùÝ¿¾ï~Ñ‹”°PÞµ°dÿ„ìûÕ/ùËhO¬øÇ¯dÚoÂÝ’E-R«…Ë™hŸýúžŸßù£|熯|^³W¿øÙG›ËSƒ•%â>óÑÁÊŒÛÌ‹7‚©2xS©Ucm}‹gŸýâW¿ù]¾V+bwþâž{{¸‹°¿D;%~uÏÝwiÝúwÞqGô¡ÒwÜyçO´§wßóKÙa+ïHu%-tÏùç?«³ŸŠ¬ùMQÌ>ðŽ7¼ò…ÿ´¿± rf¯HVfÑÛL§‘B„R僳®xÔãž,ª±¦®áÙõÝg>ÿßÞðŽ~ò_ýÖ÷Dûå½2Oþ5’ëÂìþßi^ZGþí·ýð–|ÿ{ß½ù;º¸ùæï~ïû?øá·Ýþã;ò³Ÿß%;oÃkáÎPóì÷¿ûͯîúéo¹ù†ë?÷ñ÷¿ýõ/ÞÓwÖf†»šde&Zf²ÏŒ”yqF(U>䲇?ú‰9n¯¬Æ–6Ÿòœ—½ömïûØç¾rãwoEì7¿û}8O†«0Ù”ºïÞ_iÝ÷¢xý@>ý¾áë_ûϯ\ÿå/)_þòõ_ùϯ~ýß¼Q>DÿÁo»]{¥=&·¨Žôáj‹üÝ}¿º[³ïÜðåÏ|ô=oý÷—üËÑÙ¥Éþ`eö˜`eFʼøBkŽý¯¿ÐCd‹?ÏSÞžÕØ_ù¦w}äÓ_¾áæÞñó{îE,t;,aú£ÖÉuÏ/~öíAä·åË_¾îóŸýô§>ññ}ôškÂï,^sÍG?öñO|òÚOöó×}éú¯~ý†¿=P¿ã'?¿ëž_Ýû›˜‚&ëÆ?Ü/‹Ùí?øö׿xí‡ßùÆW¾à™»§fG´ÊìÉ{ÔY&e^|!›cÚ]å-þÒê–ÞñųGÏyÙëÞþO^÷µ›~pûÏD[,\Ä‚Íô?ÉnÔ_‹ÛAQÀnùÞw´W*>ÿ™O}üš}@¾þúö«ßöÖ·¼%øêõ[ÞòÖ·¾íêÿxÇ»Þó¾|èš}âÚÏ~þ‹×õ7Þtó÷xÛïUÚ¯t5Ú_µ{ˆ?Ȥù“}ÿ[_ýü'Þõë^öœ§l.MŠ–™×óÄG?\¶ÿ)eWDšc{ôrܾºöÁ©Õg¼à•oz÷5Ÿ½þÆïÝö“»Äe°-ö·H¦=’¿sã7þóËòå°|à½ï|û[ßtÕë^óª{Å¿¾ì¥/}ÉK^òâ‹?^úÒ—ýëËÿ핯þ÷×]õ¦·\ýŽw¿÷‘/9Ê’öï|ï–ÛnMÔh²ëM»øk¨möË_Üyëw¿yýg®y÷›_õÂgí®ÏÊ=y²ýÿÉ»LfMD›câ®ÒSÞØ52·qðì—¾öjQ}ý;"Sþò7÷ÿáÏÑZçÿõ_ÁF“¨Ã~zÇm·|÷¦¾* -Ø'®ù (_oyÃk_ýÙ;€¨’l
¯;cAr( Hê$*" -ŠI‚ŠY0GL`ΊTsÎ1#‚ QPLˆ ˆiò{Uu»ÉÂí†Ü9ß¾·ÃötuŸ{ï_§NU×9µbi䈹³fLŸ6eÊdqFÜäÉS¦N›1köÜù/Y¾jí†h¼]ûÀáã§Î]¼rýVr*ÚÓÑÏTDg_É/…ùo^<MO½yéÔØ™ôõêiÏ·Bñ¿Š"™eB`ö³ Ö -Çt-yv=½‡…N™¿bóî£ ×’=yþ:¿ð5RRNìca~öaé÷ïÞ¾v9áÔ±ƒ{ãbñ†ý%‹"fϘ6aN®@%ö
:fÜ„°ÉÓfΉXµ|ÕºM[·ïÚ{ðèɳ®\Gíõƒ{©ÎHhö™“¯%`g¶púØ@÷®˜f†Újm!0ûyÀ¶”SPÑÔï`-ptó 7cñÚ˜}'‰{™G”$&§žû›Üœ¬ôHa—Î8¼oWìæõ«–‘Ô£‰cCC‚ü}Išxß>žž½{£ÿòôìÓ×Ë»ÿ€AC|ýƒGŽ7q2N¡‹Z¾zÃæäÒŽž<w1ñÆT¼±#h…ɾ¡/dT~›û,ãÞ-âÌ–Ìž2س»-ÛÍ2!0ûiO+åUµP8æì1(x✥w:›xç!vcˆ+³†…ùIM -;´wç¶k–-Ž˜55'Q< __7×Ýœ»::ØÛÛ‰°·wpìêÔ»‹«[ï>^ý<jÌ„ðé³#/]µ>:&nïÁc§Ï_Á[Ô2Ä¿Œ~úüùËg"ê×ϱ3;wt÷æ•ó§„óîidz4ÖŨìg@¼ÊOBþŽl›î}†Žœ±rKü±ó7RÒ˸1"±Âü·/sðoØxKÎá½;·nXµdÁœiaãF
÷2À»»kw'G;[Rº‡Íb2"˜L›Íåñ…mº8tuîáêîéå3Ø7 8t\ØÔYó!¡mÞ¾{ÿÑS WnܹGöxˆÜÙçg†"³“ûbÖ.ž1.ÐÇÍQ`Ý -Ì üoôˆ–.ð´²]{sNo¿ÑÓ®‰Ùêòíû™9%nL$1ò»"Þƒ7nÛ°*jþ¬)FûññòèÕÃÉ¡K'—Ô…23í`blddD•¿C›˜t0íh.*/Õ™*Ðâé=`ˆÿð‘c'M¹|í¦˜]ûž¾xëîìÎÈV¢OxÇ6vf9™ï$ž=´sãÒ9ƒy8wfšh«*ºl£G¼t§•F<{×þãfF®ßqèÌÕäGY/Ф’üLùí›Xb™Rn%&œ<·I/˜5yü¨ a½=zuïjgCJôt4512$åµµ´J‹yjii·ÃU
Û#±YZ“RSöN=\=Kó>j\Øô¹‹–Ù»ûÀñ³—®'ÝK{’KšÅŸðšorŸ¦¥\?,~ËʈÉ#‡öAYÇöí üoôˆ7]´QBÓJ+¾ƒÛ€ ³—nÚu$á:*Éò+^´ÂK£o_fg>¼{óòÙcûã¶®[¾hö” £‚|ôuwq²·p˜V梲vZê¸X§²’RiUb%%Q=Y
Mv:zHjfÖ,® ³]×½|†„Œ™8m΢ek7ïØsøÔùÄ[)ѨùúÝ{²kƒÄ‚Ùïß¾trÌšEÓFû¡ÀŒka„ÃÿV˜5bJ–Ç”5ôL¬]ÝOš»|süñ‹·î=•Dbßç‰$væèÞÑ«—DLÇ©kúºõèÚEÈeZuì`dHÊ&ªQ¥`q‰k¹²ÕÕåÄ•±qõb5uM\þ³½1òi—g¿A~Á£'M›»xņm»?wùæÝ‡Å2+*ÅÿwÏÚ±>jæ¸Àþ½ø$ü‡If#¦dy/]0„NƒG„G¬Üºïäå¤OÄC%ƨŪG)HbGöÄn\¹xΔq#üy¹÷èj+à0,L‘À´¨° -òH[¤X‹ògE´ jü·&u²I!cMmäÓŒ:`¡ lì{âò!cÃgÎ_ºfóŽ}dîƒR™á=@¹OÓS®'Ùµ fúwE῞&L2/âå1ük¥)³³³çБS¬Ž9p:ñΣ§¹yxë+^%ËîYé©·®œ=º'vÊ…³ÂÇÚ—r¬Í;éëh#)‰JY·Ñ´Â‘7¢ÓJÜZ–”dWQñЬX<\(È£ßà€‘¦Î\Iò (™‘ØìãG¼A
™÷n^<¾gËŠya!ƒ=œ:1LÑ$SM2Ae©j˜±lº÷õ
ºhíöƒg¯ÞEáØ;Ž}ùJýN×îãM7{c7,_8sRhà`/7RIÀ¢.ÄI*Ë‹j¥‹Ž T†’c—°Öš‹Šÿ+("—†„¦ghl†KžÙ;»öññ
>gFÅF2ÃûØrßäc™}ÀCfæƒÛ—OîÛ¶j>ÿ»uf™háI&¨¬ñA–`›vdÛöðò3}ñú‡Ï]OÍÈ&áÖXq)3&_ÃÛ7@å?°¯«S)ñ¤ƒK‰)–)ÆÿkÙ#“*S¢¶²§L`¡©áò Xg<ÇîÞCqŽçòõ1ñGÈŽÜ'9¯ÞæãÊØ«>BÙØµ(üæÕM2
K–2@e ‘ÆDËcv=ûùµ!îÈùâÿåÆÞ¼xš† >ˆ7BÏÂëÓ³«
anB«ÃEKN{øå¿?>j°¢Ö¨ó¾ˆKÃBkK•¡5îhÅÚ9÷ꋳՑÌ6àÜ‚+·RÓŸâЬ«fiw¯Cá䌱þÞ=»pÌÉR¨¬‘AÆJ‘Æ,¸ö®ýÇÏZºi÷±·îg>ƒBþÏøwÃ8z”xöHüÖ5‘³ÃCzº8væZ›ë·Ó¤Ên¶lѬYyÕøÝ%J¹4êÔ Jgzí;X wFênP2Û¾ïxÂÕ;$4Ë/(,D*{ù,#õF‘¸Kfìïjϵ©FÌF„(Ã3²à9ôò š8gÙæøã—nãie¥1âÆ²ÒRn\8±oû†¥ó¦Œ Ô§§cgŽ•i{=muª€°¸ ºÄ'p•wiÔÑ -R,d48ÔY«6ÊZ9ö½‡Ï_³ýйë©sÞàå±ÏÅŸ>ä¿!Ëê§l_…ÜØ¾.öh¤4Ô!…5K%&ëHG±ÌPlfÔ‘)°wé;$9³u±ûÉŽp2d¾Ïû2;#åú¹C±kæ‡èeÏéh ¥Ü¦,—54%ÓÔ7cwqõ Ccå¡s7î=ÆËc"½ÎÁ+Ç÷l]µ`êäÆœ:³ÍE¥›ZÊRb"Åç8™ië›X°;;¹ùøž:åæø£ç¯§¤?ųÌ÷x)##«lõü°`×.l3}MPYƒ#^ SÒÐ3eÙöì4)ÅcHc¢%XjåâÙê·iÙܰCûºØñMË¡“õnç22“WTVoghjÍ·ÃÎlÒœ¥v:KöSŠT–ó©ì`ìêˆIAý{Ú²Lõ4”DËe²´¨ñ™ºnFçÞæ®Œ9xö:ÒY‚%{ù4-ùꙃÛ×EÎ4°·3rcFºš*Tmàú)tRæ$R|ÛÈ93÷$‰*vÿé+I8ÁO2)•=³rî„@ïtÕÅËe²6¨jb)§¨¦clÝ©›—ÿø9+¶î?{-õq©ÆòrŸ>ºsåÔþ˜Õ§Žöëçê @nLÍ)ë·Ê¹Hf¤823
3³§ÒAWmÝ{ÿ2Q¢²Œ”kgöo]1g¼¿W·NÖÆ:jd!ÌkM,UÛY -œú;kÙ–}§¯¥ xL¤±‚¼Ü¬‡ÔRzDøˆ!}ºÛr,ŒõDn¬~Ïkh".,ŠË¼#gfÁ±íæ98D¼u÷~¦Xe(ú¿zzß–e³Æëã$°4j§ -S̆ƒ -ú[+¨h··àwõ:zÆ’è=§ïfäˆã1¤±'n_<½lÎÄà½p®v;œEÛõÍËXAªY1…]Ý©$Ñ/ùb•¥ßM<µ'zÉŒÑC=yx!‚ÿ†¢Ìâ×Á}ðÈi‘ãO\INÏy]¢±OܺplÙÞàÓË$7–T«ÿ§Vrô޼¢ª–.6äÚ/€ìI:zþ¦Heùy/Ÿ¥Ý¹|b÷ÆÈi#¹á…2Å„°¬!(™Xê›±íz
™¼hý®c—î¤={OÖ.Š -Þ¾xrÿÖ…£q¢ÈF-;.•¦Ý€Ç4”:3R6Í‚ÓÅﮌ\wäüÍ{Deïñ³GI—ŽÅ_49d€k—’)&¸²z‡ýÔÄ’iëÒxØüµ;Ž^¸ýèé+j
–ÒØÍóGâÖGN§¶œš·oWRp¢¡žX‰3kÓ
™Y6Ýûø†N[¼~g9•¡@ò‘kæ‡õs±a˜èâàÌzGôk5±ìî0qÞªØÃço=Èzùÿ–Tª±ëOõíÓ͆…3´Ksgêy‰2õM™œ=‡†N]´«Œ1ñ¯åÈô„C±«æN@SL¡ þ!,«w¨€ýxbé7nöŠmÎݸÿ$—ìí)£±u‹¦ŽêéL¥5Š* -úîÛuürrzöë÷HcŸÈú˜HcSƺ -¬ŒuI‘‰FSʤ$0SR×A*ëŠU¶x}ܱ‹·få¾Í2ðsçÚù“‚¼»w"aµZÖЦÿ[ ƒ%^!C™s_ÿ óVo?r!éÑ3<±iìÁ-‘Æ÷ÆÓQW"!£)ÊTZ㊨ù²Ði‘v¿”ôèéK¬2<żuþpìÊ9ãüú8ñÍ
5•aÀ¬G°h†K
}3®Cï¡cf-ßv0á&™X!æåf=¸u+ÅkÛ¸4V¶Z¥24Çœµ‰¬&gã]ŒùxŠyãìþ-Kg„q·g›ê©·…³þ
–j:&L[×#§EEï=}í5±$û.ð2ÓѸõ8+ÑX#˜V–§IIB2VšcúŽ™¹tóÞSWñž^jŠù8åê©ø(,óq±a—¬c4´åÿ¨eX2X -»{…-X¿ëÄ•»xçEѧâ¢ï^=}tûⱸõ‹ñ¼’h¬4»Q= ²*³:÷ñ;{ùÖýx§Ò‹7xC6 -þï\ÆaÙÄ -Í,ñ`9hÔô¥[œ»ùà)ô|ÿ:ÍýOÄoŒš:ÔéQk¬TeJøÇ1—~A“æ¯ÝyôÒ´ìWxG^ˆ¹~f_tÔÔzâS/É6Æùƒ,å•4
-ݼƒÂnˆ?u55“dŸ>¼%Ù(ãëé$,5ÊG#ÊQ öwq2eñ†ø“‰xK\~AAþ›çï&žØµnþ¤€¾Î|sM%ˆýë*êWTÓ5å8ôöEƒåŽ£—’ÓsÞ|ü„'–/2ï]?³ŸÚòç,´nä+— Ó‘cï>8tÆÒ-ûÏ޸̖¥%]8‹Ì!îv,¼$±¿ìGýÚFÖ{1m ,o=$Û{>¡ ÿeÖƒ› cÄ›—Mt¹ÆJU¦¢ehÁïê9lÜœ•±‡É¢ß»‚‚<tA7ÎœÒ¿G'«öû×Ôæ‹¶êúyN}&-X¿ûäÕÔ'¹ï -‹>áä·gn_8²}uÄÄ@ï68
C©‘k¬d9 -2¬„ݼ'-XGÍ–ß¼/,DaÙã”Äqk#&øy:rQì¯ØbCêö팙]z
=kEì‘‹wð`‰4ö±àMNFòåãqë†ûˆÊÿÎe¼öO¥Ã ©òÎS£¢÷AæË¼‚ÂÂü×xÀ<´mÙŒQ]mFÚ*mÀ•Éñò…9¿›÷ðÉ‹7í;{Ï,ÅY* -ú7EN1°—ÝO“[R]MCÏ…eCÇÌ^ƒÌ´ì×ù… -Ppÿúé=†z9ñ:êk´W&[J™ ËÎ}èØ¹«w¿’Bf–x°|õôÁÍs¶.›9zHo®ùO“äOՉĿŢ°Ì¹oÀÄùhÀLLÉ|ñ–˜Ï‘{>º}åìÑCzuaâepe2¥Ä‘Yº÷žµå@Âí´l¼¿‡–iIl_5w¼Î%k¯MÊ•üÏ£Ié®kŸ©Q›÷ŸÅkï -?æ¿zöðæ9ûOîíÌ7W&cÊ:²Þ¾ãæÝuòê½'/ó?ˆK"ïZ¿ ,¨_÷NÖÆíT‰Æ~†ÇAm&—ST×5e“såö#xaæÍû -òrÑ…ß¹zîØ¡nbW"“eYÿ©K¶¼$ŠúÉ`‰'ûQÓFtíÂ$ùý8óú'pdÿ)³Äl€L¯À0¼Ä|í^ÖKäÊ>¼“v;áÀæ¨)ý»!W†&˜Íý姸¬ŸjŒŠÈ|ÇcGví~õ“Á’Z¶$¿óé—T*ù9–) i3æçÀ‘Ó—m=tžü ûáCá»—OR¯žˆ[3wì·.cmeù–0^Ê -ò«eë¶úæ‚n("[²õÐ…;éÏqÔ_ôá]nfªèÑŽQæÅOò,Df[u4Ãtô†zP܉ÄÔ'¹yØ•å¤Ý>\Ùäáh‚i¦§¦ -»÷ž‚bÿ„[²_—º²«çŒìjcÝ^K 6cÈ -üš·RTÓëÈsö>%jËAìÈÞbGV˜‡æù—Ž y>,Ò]¤¢SU‡Zž™S²<#re û£‡öqä˜ê¨¶i…W1ÚâÿE¨EI«½µM¯Ác欉CSK‘¡¨?ëþµSñdÅ–?ã~ø&eš½ƒÂGï;G¹²Ä•]9¶cåÌQ\„m[Ãx)¨õUSŽcŸÀðÅ›$ˆ¦–ÔÙùƒ[—N9P4Xþ„™=¥?™1l{
={ÕŽcØ•½+üˆ'˜nÝ»aÁ„a½í˜8ô‡ñR&Ѳ5^¿èä2`Ô¬•;Ž£Àøeþ‡¢¢Âw¹OPÔ·fÞøaewþdOATyA]ÿøºÑ&âÊÞW–›y÷Ò‘mK§{;óÌtQèã¥, €„ýv½‡M\°qïYü ->}$ýEý‘S‚ûuZþ´ûaı¿¶‘µë ìÊH?"®ìõ³×OíZ3…þ5a¼”
d0QPÓ5Cað´¥1G.ÝÅcIÑÇÂ<ÔÍQIJjΘ!nv,Ÿ6«‡ÄþÑÔÆŒÛ»²èý ·ÓrÞ|À;~ӓΈ^æïiÏ2ñR6ˆÉ4
:»3wÍ®S×<CQqѲ|qvï¦EáA^x²FÛŸ¶Èªh‘F»²Á£ÑóšÛ¼Ì/üX˜OBÿØåÓGôïÆï¨÷¿>^V>¿¶~¾”ܼÚïàé¶8úÀù$¼û9²ïâm -³B¹Úг-~¾ˆCõ$•™ñœ¼‚&Gm9ˆ¯ò-YÍ~xãtüÚyc‡¸ÚXþ/Ï/ËžŠüãC“eµµÅünýGL_¾ýØö;²‹ÂûR®Ä‡«ÕW¨;J£2†ÛбóÖbýôu>þ™üEÆ6/ðtø_/›ˆO
Å'ÔþRBÅàeÂ/¿Pëáx‘lÈØyëâÏÜ|ˆS-ÉÄ+¯!Í=¸—-“¬õSµëµHIåLõ%}s~·~!Ó–n;|éîc¼ [˜ÿ*+5ñØv4^ö+3^þd¨FšˆŽ£úµiÓ¦Í(š~š’SìqŽ^$‹Ü|ðÂŒx´,†ãíVÝÔ¡WÍ©#êë«HÁD÷ßÒe{¿‰6Psè÷d¼|t“Œ—¢ù%Y¥‘iGj‘C5ðA¡-Z¶l…i‰hh^´hÙJNQE»½^$›‰Éðj?-ÉbÿÉ]kÉ·£¾¦Š‚\«–Í›‹º@½ô -®¢®ghÜÁÌÜanÞÑÌ´ƒ‰±±‘‘Q{ÙbddbÚÑšÓÙÙcȨéK¶8›Úø±àõ³‡7ÎìÝ9uä`g®µ¹©‰Q{CCÔtë«H€F½º. ¤¬¢†Ï^"õýDë±âèÏ/ñz Ûi¨*×qª±ˆuØûÐfäPíºrfâ² ¨‡é™Z08<°S§NB!ŸÇå°Y,&“°–èÙ,¿³CÏÁ!á8Úü6þñSqþQïÆÙ}›—ÎëßßÍÙ®ÍbX[YYZÔ_CÔªí”Óf<}ÇÎÁ[1¨} -&ßÎÅÛüÜÕd¿NÈ*úðîÅã䋇cVÌãÛ§›-qÚÆuÜjìDˆê(Wn« ßËì—:ÊǦ¥ojÍïâìêáí3pÐàÁƒðéçíÕ·§§‡‡GoâááÙÇ«ÿÀaÃÇL‰X$ެøËçO¨{§%]Äç'Í
: __OÞîn½\{ºô¨Ÿ )¶¶6Õt†ÈkÛˆ½vÜñ2^û)N&߸xꨡ}]m³®ûOM=€Ò¡¾n;-
5eEùÖuWiD”¯¥¡×ÁZàгÏÀaA#BÇŒ;ftè¨!ÁÃüeK@@àð¡ã'Ï^´jËž—qù‹ÂO_¾~)þ‡’Þ³6jî´‰cFøû
æ;dð úêâ^} ^ÛÑÙÅÍkpи™K6íÁ©q¯Púü ]kú‹Gv¬Y0y”Ÿ‡k·®öuܪï%:47ë`d £…¦ru¥2js}r®¤C¯~ÃBÆ…OŸ=wÞ¼ysçÌš9cÚÔ)“'‡ËšÉS¦Î˜¹bCì¾—n?|úê}Ñ—oß¾~þ˜ÿ2ëÁ‹'öÆnX1{ÆÔ)áaa“&N?vL½õ -#‘‰3ÐŒ'tÊüåë·îØ¿gOüî]q;¶ÇÆÄlÛ¶U¶lÛ»#.~ÿ‘S®Ýyøäºë_¿ÿþûoß+{õ,=åæå³Çíݽs{²dËæèM7¬[[O=@B/ª± ·=}Ö¼EË×m‹?vþæ}Ô¥>~þúíêQ¯³Óî\9u`Ǧ•Qóç̘6¥.;½€e8ÈÇË£gW®¥‰®(‡ºŽDF¶Òuvñ ‹X¹9îÀÑ“gΞ={æô©Ç;zôˆÌ9zìÄ©3ç/_KJMšû¶àÓ—ï¿ÿñÇïß¿~*Ì{ù,ãþëWΟ=uâ²äð¡Cìß·'¾¾z€$lÙRcˆÂ~{ùʵ·Æí?~þzJzö|¹¿}Ç=êåÓGwÏÙ³}óúÕ+Pó:î?5ô -Z] 1± -¿ý'‰Þ½zž•þ %én^Ï= -]*îP_‰ã–Eª±
>öøþ¸„Òh’n JÏ«Ä(ɵUÇç̈öjÞNþî£(WP\‚‡Ä#*û$8b)åŠzì’@¯`Çý„U9nYu šz@aÁû¼×9S¯Ž§Ê¿0Œ¨riReXbh¬DSPÕÆ§Kö&eÁ’çæúú›(ø®WD7ûÿJßýrü%¢¾{-huÊoÓsÜõØýð`qþà–¨É}Ù&íj•J$ök³æ8OHË -?ÿ㯿«~ä
L=ê½6Ð’#õÖ*.¯¡z -Ê‚ÈyOê¥åG$tc¢‘RUSר#SèèÚÏoô´Åëw%ç}“Ѳ¡o -P¿•ýñýsá›§÷ïX9+]‡·b •5•$§D$12Rªh´34µâuéæ10hÜŒÈu;'àCòò‹¾ü"û7‚Töço_>æåÄ©ýû:ñ-Úk«*â}Ø´Y“Š#¥»s×^Þ¾#Ãæ,ݰóð¹ë©Ÿç} sK-ÿ}`‘ýþõS~îã䋸ÎÐÁnö\óöíÔÚ¶iS—hÉŒ’XÙ‘R`ïÒgððqӬܼûhÂõÔŒœ7Ÿ¾‚#û—BÆËâÂ×Oï_;µ{=>¾ÖÕŽcnDm÷oÞŒNf¹‘RÇÐÔ”½}üCÃç.Y»ïÄÅ›÷ç¼y_ôåûàÈþüóÏ_àñòy:>ÊjÕÜ ý]íy–&zZªÈ™Ñ3E?"ášÔœ’CFÊg.^½e÷‘³‰I2Ÿýþ'8²'d¼üö 'N]?³ËòÙ}Ü…³ö:*ŠòDé¾?”™èG¤¥sJj¤œ¿bÓÎ'/ÞHIÃÛëÅ‘ý+¡æ—òžgÜM<¹gó²9‡òèf˱4Ñצ1«3Åër -ÊêíÈHÙ½÷€€ÐÉs—¬‹Ù{,áê™9¯Þ‘mš ±/"WF§.ŸØ³yżð‘¾^®BfG#]M<f¶øñ˜I
•-HY4§ätvrë‡FÊYx¤<|æÊí{éÏrß¾ÿXü4ö¯†Z*ûR”ÿêéäË'÷n]½pÚØÀÝ»ð¬M
Û©++ʵjñ#gFi¬u%äÆÌ—¾dN))ï>ÊzþgpP[Ÿÿ‘ýk¡\Ùçï^"•]9}`ûú%sÂFë׫kg¶…‰¾–šRé ²#ûµi‹Ö -ʺÆÛnh¤=yîÒõ±{“‘2ûU^Éàøƒl=iè+âÊ~ûVŒU––|õܑݛW.œ>nøà>.ö‚2€ÊCfªYe
=+¾}O¯¡x¤\³Í)ÑH™A”_Jvi6ô…
¥2œú*;#õæÅ“û·¯_:wr¨¿Oïn¶\Ëx@-ÍV2q½ªr‰µ°«›OÀ˜©xNyðžS–Œ”¿ÿù×ß0Tþëùçï¿q.bñÇü×Ï3ÜI<w$~ëêÅ3'ŽêíêØ‰…& -5Öaÿ5PÊ'+UJVª¼ÿú2ì¿$£¦d¥2"#û¯·‘RS²’xÿ5Ùäï›ü)¨)Y©Ìþë.î%›üAd -€Z@¯JÔ) - -DH
ÝÊQ 2@jèVŽ‘RC·rˆº•£ ¬ 5t+GÈ -DH
ÝÊQP; -º•£@d€ÔÐÊ - -”RC·r(¤†nQ(PHd"ƒe€ÐÊ - -”R#©È @ 1ˆ -”Ò!‘È @
’‹ -”"™È @ ’Š -”#¡È @ 9’ˆ -”RA·Ðâ @ -Š -”’#©È @ 1´«yB2@Z$ÔŽ$„vÉX %ôKÆB2@Jè—Œ…b€”Ð/"¤„~ÉX %ôKÆ‚È -‰EG’ -IN"‘R!ÉI„ 2@*$9‰DH…$'‚È -IN"‘R!ÉI„ 2@*$9‰DH…$'‚È -÷÷ - - -:u¶µí -èýŽþ -˜<lú>‡>‰Íåc#ð{XV2Œþ%—ÉAWÅáZñ¸lú$– -ÝjŽ>‡/Ý5K6~\ü 8økðãD_ƒãõ¹&ºxù(‡ÇG/Ñ×ò„Ôcfñx<t_Xž›EîºY.¹1,!ÃJÀââчð8L¦~eË ›;zË{ËõMÍô}ú£¿ˆåy¢ˆn8\¡ -529\.þƒÃðùèSÀÑ\ž€Çâ“?Ð]ãà?6“ƒ…ޝƒÃÁWÆd¡À`qÑMáùVL>]}ƒÙÏAO]À@_dÉE—‰>¾Ÿ<‡Ç@O=5~h|¦~¨<‡ÅEßK–Ýô)—ÉÅ7ŠÍD’Áů¡ƒ:#—‹¾}O(º)H³Hmè+Q‡à…ú,ÔxL,½ËúɳпeX¤3!Q1øãÐE™ú8tK±%l.]î`è± -˜\!1…d(DÈb#ÁqyÄb¶ºô>d:êä5$\ªÃ²Ùèa°É•ñpÿá’çÏf¡ÇŠî~ÌLôè3„L~
ÅB’*cshU·8”(²‚ϩ܉FWÕ‰B«èD¡4;Qx(”n'ªÂå…VåòB«¸rÁVL}&ú'r2l"QtEèmè6XaE’×Jÿàr„HLèÎPFc¯G¬f3ØøÖT¾<ôÍHüÙ–\ÿElDnËôb¥ÏÄF"Ñ˨±y,òU\d3_ȧ®œÁäñÈW¡ž/àñ©{ÄyäSYXÑœò/’?¤9‰›/$/"%WE_*ïè†)Sï1cüFêOð4fvòfD?BJB””¨ÿv–·D7ƒ|ÛŠÁB_ÄÅæòðýÇÿ}!“É«x>£E-Ù\>º»È\ä ~Ø–ÏÂêçðøö:HûµŽ’}-i+¾Cè[qgrtDC:õ&‡ð8èýlü›! NÇæ -‰úøè0«ø÷Aä̘B.ö/,ú&‹tBäüøâ§Ïå“§^2¢ÂæRÏ;G._XþÅʉ…öÎå²PÇ~Çgcó9èf’ë@}•Oú‡ºèŸ4TテƒCuä.,f™[;ºœüæYlAyM– -µ[ùKe -¸\ÑåW¾T¤~0+\jÅÖ¤Ké;ŒÃW†ì'£ƒÃ ìG•òb&¹VÜóÉ,ñ5RÎ -Éå‹ß…½—Yö“J^!n'z…¢4ôòôK?‰ÅdPãPé–¼„Z–ØUú¾ëK?òURò&·=^ñ< ßLä@YøãÉ¿bqø.¾Ï,,‹’â;ÈC‹Å"7}6 -ñD]L dó°
|Üvø*ш' C¡Šøf°9”a(Üă ~ù]r‹P×ÃãJ_2] -z©±…¢W|™,—pñ‡Þ†n?9„©X½u¢4.=>Wt©,>Z™×ЕaŸ‹d1 —XV|tÿÑKhÔâñYe^ - A<‹É”}º\ƒÇ,ûi¥/‰¾µ,y
ߺú¥Æb¢ -
(GÁzÀe¿_9î],‘V¸èšª0d* Gx(±‚„‚û -…$šæ0ÐÕãc‘*÷‡ÁÄ#% -2„êçäH‡BBC½’EúŠîù,|ç™ÔÔÂlü
èY8tc
ð/GúD—‚æmòñDÍdH$…ýG¤(. -8Ôk<(àC6Pã1ø;Y(ÜgrEý+`ã†èyã˜ÙÁD.=JÒ/‘,¥—†g¢ž@Ԓăø¢Aè]l*E¯à!
*XsÄWN‰Ba.jØMϲх&h:M¦âÝF„u¢þt -1F4/GC!zOwyk§ ðA}º;êû¸Ë*‰l8äV x}„>…‡h
©ú
øÑ›Dÿè³q¬Ä'ïF3=üyccb -¬j´†U?»U
kXVU`ÕÏbU£5¬«À*° -¬«À*° -¬«À*° -¬«‰I`XVU`տǪ†5 ¬«À*° -¬«Öª†5ìG&UÿV5”aÕ›ÔhªÃè˜TÏVÑ4©ž
“Ȫú1LR“dm˜töÈÔ¶Ú›T·¶Õ¡=µ´P¦–üËiªc’ÕX½véOÑ{©òÅ¿Ô\‘–¼ÿ—²Uµk(”Ü„:R i³æ-¨bï5Õ寧v£··j-‡Ï @´¡ÊÊ7¯º¬<õèýrm•”UTÕä´ƒ6rª>í -›—?E\EkUíöæÛnîýû 6È«W×NLS}MÑá–Mʵh!×VCϔٹ›ÇÀ€QãçN›>~¤ÿ€ÞN¬MðQ…åö%çFË+kQGú›:gaä’¨Es§>¨·#ß‚œ YÖ,rX+|Ò´uçÞþc§/\¾nÓæèõ«"gO>Àµ«ƒŽªB«²gnR-Tu:°ìܘ²`eôŽø½{vŬ_2{¼_g>>o¸u¹£`E-ð‘é¾cg-ÛwðøÉ“ÇöïØ5}Ô W[k#-%¹rÇ`—¶pìã?!buìSç/_¾pêàö5ý=ØtÈéìe[üÒ¬eUÜ"`ÒÂõ»Ž¿zëÖõK§ön^25¤_7~G=5…–ÍÊ^:>þ]y6j¶hãž“WnßMM¾qáÈŽU³GêÙÉÂ@]±Â¥£ç!¯¬m̲÷ðŸ´pãÞÓW“ï?¼'ñTüúùã}Ýl
E—^V%Íå”´Œ]܇MX°aÏék)ÒÓRoœÛ½8ÌßÃŽ‰/½l|éÍ[+jZuv2vÞºøS×RÒg<¸}ñð¶%S‚ú8²M´•Ñͪt{ÕôÍ…=†Î^wòjJZæãGÉ—n_>=ØÛ‰Ó¡]…SÆ©KWÕ5ã9÷1cÅŽcW’ef¦ÝM<±såÌ‘ýy¦:*ZP—Þ΄íØwøÔ¥1G.Ýyø83=åêÉ]kf‡èÎ飼ZþÁ—ÎD7+<rËÁ· ©×Oǯ;f‹Ðœz Z´n«ihmëæ;qá¦ýçnÞÏÈL¿wãÌžõc£¢¯†´øK¹›õK³VŠêèf‹X¿çÌ{éQ‹³{7Ì?Ôµ³%y„[´TPÓ3ô8zîÚÝäö¦ß¿ynÿ¦…|Ýl¬4Ê?tréäfñ»ùŒšµ:îDâÝG¨Å„Ñ‹&s·µ2ÔPl]©EyS®s¿3Vî8ŽnoFúýÛçn^æ×»‹u{Íò2)¹½8]½‚§/ß~ìròÃôÐ/ÚŽdÂ0ªÜâ¿È7(kSdYÌQô@P‹$,“ÉžöUKô@ŒY}‚¦,ÝväRjñðÎ¥#1K‘°XÆÚT‹ -„<BÏÀÉK¶¾Hµ¸|4vÙ´á^b)Vñq‹€ÉQ[I$Å+Ƕ¯˜R•KZ0ì<£¶B-?IqD?gn%)Š[´G-°¨™iHŠq«gòéÆ3«RŠ¥-¶P-ÒS¯Ú½fN耂ªÅKµè-n‘ù$ãÞõ3ñëªo{ë.â2Ÿ<¾óìÞ‹·r‹¬ÇnÛ·qþø!D¼5µ¸óèIVæÃ[ û7-˜0´—MUr¯Üâ铇·Ïˆ^8Ëݰ’Ü+µHC-%!ñ.šTµÜ+·Èz†Z\@rŸä玽"Yî ¹‹:½iw.¢"jQ±ƒT׿ÊNØÀ-«mQ¢D ZþqXÂQ´÷Ú×’é¶{š-J½Ï‘ËÉéOkn!òpS–ƽ|7ýivu-°Ol!§„FtǾAS—Ž’’ñ4ûiµ-~ù]ä©§!O˜’ñ,ûizòÅÃ[«iA|»“wlO\M}œƒZ\ª¾?Ðð<r檸“×îefç<«¡5Fu÷5{õ®S×ï?ÉyN£…^GjŒ?sãAj‘QM<Ö’0£çà±óÖï={ëáÓç/²3îÖÐBÝÀÎãçoÜ—pûÑÓ5´@¡~lp}à|Rú³¹5´w(¿I‹7º˜œ‘››ó˜´¨²R-¬ÄÒ½›‘ƒZdü°E“J²JyœóµH¾ôƒ~^*28cY=Fß‘TRêá*· -°Æ¡ñ£âhP¶EðÔ%[&ÜHMËHtùøÎÕsÆ Ù²BŒ\Ò‚ÓµoPøâM{N]¹r/õε³·-›1Ò§‡À¼Â8HZÈã0ÜÁÓoBÄš‡Ï^¹qóFbÂÑ]…y9áñ¼\ä^†3»¸
=siô®Ã§Î'œ>¿eùì±¾öìíÈì ÂóPÒjoÕÙ¥ÿð°ˆÑ;÷:|h_Üæ•óÃ}zÚ Hù¸„jÑVÃÀœ×Õcè¨)ËÖmŽ‰Ù¼nùü)¡C=»òð,G®y¹$pWÓíÀ´íáí>{á’å+–/Y8;<t˜W&š®)´ª4/jÙÏÖxö®Þ¾!cç͜9cZØØà¡^=í¸æd¶Va$f©ëšXñí]< -5jdH ¯Ï¯ÇSáæÚ(kêw°âÙ:¹zxõ0p@¯Þ?<‰œ:MžµôM,X[§î=\zt«îXiò%x¾©Û¾ƒƒÍ…B>—õã3‚ÅszEeum]ãfæhRoRͯ•Ö
ôõõõª=½³üÚ„ªššºzMG1–¬´Äë - -äX½êÏÕ£ÖX~¥ÖXZµ¢±ÈRv§Y3š'^‰–ÄG]Ñ:¾¨ÂzͳhÁÁ"5û¯¹Ê?.MÉ©:½,6©ûIŠ÷3˜Ô‹B×ÎÅu…|Rù“˶2Hõ~\ß”Tˆ²q™Q\°ž*ß˨ãùTmlR®“K*ñsp
_Ô¦Šo'‚Xû½Nk*R5n<qMEnÅšŠeÞPRS‘+þšŠ|.ÔT¬/Ïo
ê§™FøcVÿÊVKÛdaO-m“µIÒV?VIdX½™Dß°z6©ÑZU£a
bR£µªzÃÀ*šV5 IÖªVÑ´ªÁM«ÀªU?‹[ -¬úY¬jp“§U?Ëã«À*° -¬«À*° -¬«À*° -¬«À*° -¬«ÀªFnUƒVU`XVU`Uc3¬«êÙª4¬ZU½IÖª1ì絪ž
£iR=֒Ȥz3L -«dm›Ô&ÉΰZš$Ûêʤº2OöHm^=CÇÔ†¶â_DCdçÿ§L -0ü¨,fÓfÍ[¶j-''///''×Ål†«6TÕ7hÚ¬EËÖò -Šm•”JmÚÈ·n…UUHBT£µ¼¢’ªº†–6BKSCMEY‘ª¤ùk¥zⲘJªšíôÚ›˜µ7ÐÓÑRWiÛWÐøµê²˜ -ÊíŒÍ,¬™,‹imÑѤ½žv••4ÉWˆÊbv´æmìììíl:ñX–âJš -{‚‡Ú(kè™Xr:;twu÷ðôèÝ«‡S!ËÂXOS¥RñQI5cKž]÷ÞÞƒ|ýüý‡
öéÛËÙ–ge¢§¡Ô¦Uó_+¹lÕFEËЂkßÓkHШq'M?&Ä`ûªŠ £Z+ªë™²º¸x5iúÜùDÌž:~„¯—KNGM¥ò…VˆQòJZí-…Î}‡ž±dÕº
Ö®ˆœ>Ê·o·NÖÆíTÛ´*[S\’TÅ9eÁÊM±»âããb6,˜<b»=ÇTWë,××:R×ïÈwö -œ8oå–øCÇO<v .zùÜ ^Î‚Š…4›ˆë#Zuî9pÔŒ¥ÑñÇÎ]J¼rñÌá¸
‘SGpélÕ^³\!MqÝc¦]ïaã#Öì<rþZRròijcVÎ3¤W¥Ò›%uˆØŽ}Ã#£÷¹šüàÑ£{·/ßµ.b¼¯»£Š¤âϹ_ÈtRUèÁã¬'é©×Ïìݸp¢_oªÆe™Š•¥¥§º5kÕθ„VNö“·öG/šT¶*f“Ò›K•žr4fîÚøÓ7d=ÏUGZ< }Gʼn¢ÒSúTaÈ
{ÏÝzô,÷ÕËìô¤¸ž’Ÿ{åšMÔã0°´é5tü‚MûÏ'¥ç¼zý*'}Çê)‰‹UÙRŪ.ÜÉÈyõæúŽ\Áoh¯Jå'q\ÛÓÚÖ}ؤśþ?{wU¾ÿüîÝ»wW×î[D‘žA±»ÁîVìn]»»»×îîîÄ»À@±6î/þ¿ÿ÷{Î`~QC¼ÞçïþvYG`æs>Ÿï93óCn>|"ná{vßgv ÔnaÙÞª÷Ø…›_”·¸wýÌžÕ3‡unXÑ\ {ºdw"Œ¸_\ïqr{+íw¯Ú½jÆŽõ+˜>Üñƒ½Ü¶ñ¹õðÉÿ»WOî\1í“ÛFØ™•¶3›ÏGâw®œØ±|Ê OíóøáZG/É[<º}ùøöe“x×.óá^’îÌ¶íØ¥ÛÚ-.Û¶tRÿ¶µäî“¢¬þõÏ÷‹=|g¶íÇ/[nqt«u‡Ë÷÷Äühg¶Wîø‹[Üò·ÛEóãmº,;{ÊÙN^½«ßâÈ–Åú¶’·øx+0}«ÕòÚÎl»O]»ðÔr‹ñ}Z}¼·§õÌt:sõžÓ×ï=Öo±yÑxm7зÓ÷4•‡ÇðYköžñ½ÿøÙÓ/ÝÂr@,R¹qWyxœõ{ðÄz¹ÑÚgn¶—›vx¼w‹OP?†P=FÏ“‡ÇçOý¿t‹»¿™¿ñà…›ž>ó¿ýÅ[h›åÊjŒ8 .FöâlÑK; n=zö<·?}nù+o!(Ë-Æ-ÜÙ[èÛBʃöˆÏmq‹À¯¼E`¬¼Ed~¯¼¯¾þñø¶Çü+ëêkk÷ëo:¿tœjëÂÏ÷’ÏÝâ£~`íW_¼E„žxW}‹Oô݇ª[|ØÛ?¼yñ‹·øp~<–·øÜ擟˜Q·~ñÏA1k¨n¡mW©í›*gí͇”·ßÍU¬ -ºW¨çÝo윕[ö:rxß¶5ó'h_¿¢»MúäÝ"lÿæ]‡Lš¿rã¶;¶¬]2}d¯Ör£ÎìŸØŸ]üê©3ç.,7Þì5lÒÜ¥+׬^±hæ¸A›ÖÛø§M–èý[ü ?%!e›®¥ª7j×kȸi³çÍ›=mÜàmêUò(œ;óŸ`þ¹íÝËz6öî>`ØèqãÆØ£]ãeLv92~ðááÛhfÏçP¤\-;tëݯ_ßžÛ4®YÁÃ1_¶ÎÔþ~^›1G~Ç"¥«Ôjؼµ·wÛVMë{V(îb—3SšdïßUÖm4M‘6SŽü…ÝŠ•\£V]í"Ês-”'kú”IÞߨóaççâ&6yìM%Ë”¯P¾\ébfyêœ!µvüÁ-¬ÛhÊOºÈöIŽ…låé¹8×þàS+"Ü$yªt³ÚäÊ«šFÞ\6Y2¤þø|Þòsi×2´K2eÑwÞÌš9CÚOoÔùðm4å唩ҤÕwÞÔ7Þ”×%>úÖm4Ò?zDÛy3EòäI“„mÔù©9ú·Ñn$?ÝäW¹õ¦vMæóûh¾¿õæÏá×}¾|yɲõæ¿ô|éÚRąIبó½}ËΛÆlÓ©åû÷êt2»Ú;8¹;Ë3ìÝÌ.6“:¹™íÍNNò‹Žîö®ÎfG›Nâ‹.öâŸÌr¯N'{w““üsnÎÚv›îŽöNŽ&g›ºâot¶wpp7¿·W§“ÙÁ^îqYÈÑÙÝÞlßåßX¹U§üu>±hgñEWñ×›åV ®âŸL.6®ŽŽöfwG¹µ¨³ƒ½ƒÙä.þ~Wñ»j¿˜ƒ‹øœm\Ìîâ»;‰ÍÅUüdâ.â?9¸»˜l -9›í]Í®.6e’:»ˆ_ÞÁEü¦â/ssßÉÙÁÁÞÙÍ]üýf³½ÉÉÍQûÕÅ7wu’¿ºƒ»øóÎâ.2‹¿Ìì¨m{j£³ø1"ÜEâï?š¼;>º×äƒãdorw“¿–»xœLf'“«½“»«åK&'¹Ã©“›Iü9gwý‹î®n®6òKÎn®Ú6ªNönnîîòïsµ“““~79›ÅÝáÏ9‰ÝAü“øûLŽâ‡1k¿‡“½“³£øs&“½‹³YÜM&ù›‰;ÎÙÑdïê(ïqǹ‰"±i•ÔÙU|ͬmÉ*JÄAüÎârt“¼ü>âNíŸÔÅYü!'í·r‘?¶‹ü\\Ä}äâ,þJù#賋øLâ'w¬«“£xŒåÍÜåÃ"‚þIåf±úžª¢†œœÅýaûXgyWˆ¿µ¶Í¬µ<ä–åñþ6³ÎÚßfµc2Yï[“£ÜcÖíý/ÉŸÍù£?÷qUöOZºeRG›üuºtiѹMkï-Z·oÓ¥—‹›vô¹ëǤ~lêÿ[Ú;i!Q¬2fGñ軹»ÈÇ\”šÙÍd£ÿ'Gyà9ZHëOP¿³å¦Î®f³|ÐMnŸ¿øéÍÎâ¨5¹¹¸Ù”nö}M6ò>’;ÍFîû–þÊï+ëÅA/ñ]ÛŠcºtéÏë.nnöâ‘G‹›è.²99‹CK|QüüNâ5‰;ªªøš8ðœÜä—Löâ[»ŠÃS”°“³‹ü’›øSŽ&'w“ü'ýO™\Ä?•Iêâä&Ž'wy°»Ù;;Šúªú©ï[õs¦£³x4E¹˜åCèbu`#š¡»ì=âšÿ¯Ðûÿjr1Ë{ÄYügggñÍä-µ]ÁÝQþ›»ø½ÌâØ•}ÂÝ,:‹³³<\Åq%~n7wQf…œåñíêâ¬U€¸øm]\mÌ®âÐus2}â¶N²Ù¸8;j½K|'³èJ²¾éÛ–Žô·•¿®É¤µ8ÑLÜ\"V€¼ËM&Ù×ä#":“IÛ3ZöX³|Ä-Ä}ä.Wgqï:»k?‚£½øž&›_ßÃUtE›7ÝÅÍÁÕU>Øòq5¿ÿ°~¶ -‰Ešìäî”ÉŽœöfggù¸Š¢ßU¹ÆtpÕÿœIäáÃC>ÒN– vW¹Ü‰p´ö±þàö±–mA<Ö⛹ºj}A¬ÿœÄcí,–æo~¬\ä¦÷rq*Ž -w'mMc/Ž`Ñpeçqw’ûÉ›äMþ>îbüºŠËGÛlrÍÛÍÉQ[ÖËE¡üÝÚÝIÜ3ÚZÆ$[¹˜ëÚ£ý߸t俱“x\´®"VèâÖŽæ÷VáçÿSžf½-ÿ÷ánþÍ"îçßì£ýó4³î韧Ùû»úçi¾m¿\кÚ4±q“íK>xrWÿOýùoÖ?dýÿŸÞÖ¿Ùûû‹SQºlíO!„B!„»}ïGŒåo5#„B>£†#”BH,áséI!$vÆðaÇè$„;cøPctB‰1|x1: !„ÄÚ>§b£B!±(†O¥ØÏ臈Bˆ‘1|Å9F?b„Bb.†xÀèÇBH4Æð)ÏýxB‰â>Yâ1£[B!QçIÂaôCM!ä[bøøH˜Œ~Ø !„D6†]„B¾ÃÇ"2º!„|ÃG>ÇèÒ „"cø8€’Ñ5B! =†DžÑÅB! 1†7|£‡BPïùøF—!„ÄÿÞêUŒ.%B‰·1¼Ã#j]P„ßbxcGô1º¸!$žÄð~Žèft‰BHÜŽám1Éèr#„8û7bžÑEG!q,†÷mÈèê#„8Ã{5b£ËBbuïÒˆ=Œ.FB‰¥1¼?#2º* !$vÅð¶ŒXËèÚ$„Xû1b?£‹”BŽá}q…Ñ¥J!†ÅðŒ8Çèš%„˜Žáq”Ñ•K!1Ã[.â4£ë—B¢=†wZÄF2!„DcﱈOŒ.gB‰–Þ]/]ׄ•1¼©"3ºº !$jbx;E¼gtBÈ÷ÆðFŠÂèJ'„oá- ŠÑõN!ßÛ' £«žB¾.†·M$XF×>!„D6†7L$pF„¢ŽáÈÅÄ$„ÄîÞ$ãîÕØp÷BHÇðö;qŸÇ×;ŸB¾!†7ÆØÃ臂Ç"v=„Ã[¢áŒ~>Ãï™ØÀèBdo†4áHÆð{Œ‹’cx'¤ñ~C¿yÔ! *†·AúíwÆð;–Gïcx¤ÍFa¿Ÿy( !ñ2†·>Zk4Åð{ž‡•obxÓ££Æ@ x| !q:†·;iLÆð…G™Gcx»£…Æ|€x¸ !q+†7::§1üÁâq'„ĉÞâh˜±!†?p -$„|!†7"š ‹á•FB>ûmŠDŒáõFB>Žá-ˆE>ˆá%G5B>Žáý‡îD>Žá…GAB"ÆðæCk"Ÿ‹áåGMBÂbxó¡/‘ÏÅð -¤2 !a1¼óБÈbxRœ„ÿ‹½ˆŽD"ÆðR¤8 !†·ÚQÆðj¤D !†÷‰L¯Iª”„ÈD>†—%UJbg(àhá†Ç‘|m/K -•Äž^üˆŒ.L+bxR™$6Äð²óâG‹ˆ¾ÆHHÄ^ê -bø} -|”LÖWÒdÉS¦J&MÚtéÓgÉø~´¯¥OŸ.]Ú4©S¥L‘<™¥Ôd¥E(4£5+^arBÊÓê+MÚô2eÎ’5[v›9sæÌõqÄWsØØdÏ–5K¦ŒdÉR•&Mt4êŒXb©0ÙÂ,–"ejY_Y²ÙäÌ7Ÿm»‚…ìí*öö… -,`›?ož\9m²eÍ,JMVZ²¤–B
:KðÑ›˜ìa²Â~M":˜(°Œ™E}åÉW ½ƒ“‹›Ù½HQbÅŠœbÅ<<Š1›\]œìÈŸ'WŽì¢ÒÒ§MRëhÔ kb²Â´–*,°¹òÚ´wt1)V¢tÙò+U©Zzõ§zõjU«T®X¡\™RÅ=Š˜\ -ÉJ³É& --MªÉ“~PgFÿ¾$ÆÞÄDKšL´°™²ÚÈ+ììæ^¬d™ -•«yÖªS¯a£&M›5oÑ¢EË÷#¾Ò¼YÓ&Ö¯[»fª•+”-U¼¨ÙÕ©pAÛ¼¹l²fÊ šµÎäúŒ2KhÑKLobI’¥&ZXîüv…ÜŠ/]¾R5¯:õ7kÙ¦]‡Î]ºuïÙ³Wo‘>á‘ÿÚ«gÏݺvîØÞ»u‹fÔ©Y£Jإй»9¶ËŸ'G¶ÌÒE¬3¦fÂJX‰É&–\LÉLÙrä±-èàê^¬T¹ÊÕkÖkÔ¬•wÇ®=úô8xèð‘£F;îãŒ3zÔÈÆü6 oï];¶kÝ¢Iƒ:^Õ*•…æêXÈ6oÎì™3êu–èÚYеÄ,M,]Æ,6¹órt-R¢lÅj5ë6jÞº}—}>zÜÄ)ÓgΞ;oþ‚….z?â+æÏ›;{æô©“'Œ5|ÈÀ~½ºuj׺Y£º5«U,[¢¨›“}Ygbn¦H–$b;3ú ш%–<ešô™³åÌgç +¬rÚ
š‰ëÙwаQã'OŸ5oá’å+W¯]·aã¦M›E¶„Gþë¦M7¬_»zåïË/œ7{ú” cGØ·Gçö›5¬ãY¥\I““½mžœÙ2‰ó -~©˜¬¯ÿüç¿Âò™ûoZ¥i…öêepPàÿwE?utÿÎMk–Λ6nXÿnÞÍêV/_Üä`›+[Æ´)“þ*›Ù4³xkþÔbRÚ6•¨àÙ eÇ^¢Äæ.]³e÷¡ç.]¿uOô° àÐWoÞjöŸð³¶«¿ßŸ”Ú—þþ¨Ò´B£ó…^g×.;qhÏÖu¿/˜1a¸(³¦uª•õpµÏ—#KúÔÉ“$ÖN -ñ†ø×Wú™§õä ¬ÔþÖš¬³ gÝ»yÝçìñ;7®\8sü°¾]Z7òªXÒ,ffÖ©SÐÌâ|,£RœTŠÕ˜hc®ÅÊ{6hÕ¥ßð‰³—¬‘%æ#æ¤ÿ“çÁ¡bVa–¹gYaÉëòbXà³gϯĊ ”×Ð^X/s¼•¥ÖÕ¬u&ÖgÏŸúß¿í{ù܉ƒ²ÌfŒÒ»c‹úÕËs-$Î3ÓéÍŒÓ̸}Tê'•Z+]¥N³½†Œ›±hÕæ=Z‰ÝxjmbaöGøE‰À§Ok—õïß»þ¤Ò½{÷ï?О
xòô™¼`+ÏH#TšåœóÝ[½=Só¼(³
+æOó[vMÅÌtw, ›Yøi¦Ñwù†XFeâ¤)ÒfÌ.ÚXñ -žÚt8jêüw:uñš(±gArNþ6ë¬úR»è¥]È¿s놟ïõkW¯\Ž+W®^»î{ãæ;wï?xäÿX^¼µ^\³^ü°,Ï^½|øøá]¿+çŽØ±~ùÜÉ#ûwmÝгBqÙÌÄÊL^3cdÆÍXFå¯ÉR¥Ë’ÓÖA¶±æû›8{éºíN\¸zS”Ø‹—¯¬sÒÚÂäR*8(P»|/ÊËW>û}îÌé“'Ž;v4,ÇŽ?qòÔ™³ç/È'Ñ}oܺ£=
¥=M OQß…]ÃÕþÊ—ÁOD™]>wlß¶5KfÚ»CóºUõf–^ofŒÌ¸m9öÓ¿ý*Wü¹íœ=Ê{6mlÌ´…«¶ì=vîÊ{þOƒD‰YN'õ -{ûF»Èõ4àÑí‰ÈKòÅÇÜ¿w÷ÎÛ·mÝj}ÍâÖÛ¶ïØ¹kÏÞý=~ê̹—´'Ôï=ðü40èÅ{…&{ãëPYfw|/9²góÊÓFìÖ¶‘—Ö̲gL›"ibFfÜ‹\Žig•)Òˆ¿½©dåÚÍ:ô6qÎò
»Ÿöñ½óH¬Å¬%¦/ÓßÊ˨ÏÅé (0¿k—µ—TÜ·{ÇÖMäË_—/[ºdñbý¥×‹/Y²tÙï+VY·aÓÖí;÷ì?xäø©³|®\¿qëî}ÑÒ#t´?µsˆ×rh>¸}ý⩃;×/›3qXŸŽÍ늕™ƒmÎ,éRÉõ?U·¶K™.sN[Ç"eª×oÕuÀèiWoÝüµ[‹3J}-öwXÓž’O<^¾pöä1ùâ°-Ö®\¾dá¼93§O2iâ„ ãÇ7NüÏ„ 'Mž:mƬ9ó.^¶bõÚ
[ä‹e¥]¼|ÍïÖQh¢£ÉKoÚÉÀŸ–µÙ³€û7¯ž?¾oëêEÓÇêÖ¦¡gyg»Ürýÿk"y–ÉÂ,Î$|9&Î*휋UðjÜ®ç ³—‰6v沘”Ï^„¾~÷‡¶:ÿë/}Ñ$zØÃ{·ü®úœ;% -lçÖ¢¾ÏŸ=cÊÄq£G<hà€~ýúZß×·o¿þ
2läè±&O›9g¾|¹ö†ÍÛwí;tôä™—D¡Ý}`yšJ«³?´gB‚ž>ºëwéôá]–ËfÖ¡YíÊ%MöbýŸ&…v–ÉÂ,®ÄZcb9–-O!·•ë4ïÔo䔫¶î?qáúí‡O‚B^ë“Rob¡!A²‡ù]¹xöÄ‘ý»¶m\»b©|Áþ„1#†ê×»G7ùæÊ¶mZ·j¥¿±·U«ÖmÚz·ëЩK·½ûømèˆ1ã'O›5oѲ•k7nݹ÷àÑ“¢£]ןp¯3miö\ÌÌkNì—ÍlôÀ®ëW/SÄÑ6gæt)Y˜ÅX®À&J’<MF›|…Í¥«ÕoÝmÐØ™KÖíÔÚX@ >)µ5¹þ¸?õpÇ直°Ãûvl^·ré‚ÙÓ&io=êÙµS»6-›io¯UÓËËÓÓ³F
ñ?^^5kÕ®S¯A£&ÍZ´öîй[Ͼò-tã'OŸ³`‰hi[wî;tìÔùKò…þbª½nè6•Ÿùß»qùŒÖÌ&éÕ®±W…bÎvâ,“…Yœ‰õ´2IŠ´™rˆåX9ÏFÞ=‡Nœûû¦½ÇÎ_“mì¥ÖÆ"\Ã’Ïü\:'+lÓÚßÏ1iìˆÁý{Ë7Q6oÜ n-ÏjU*–/W¦t©’%KXR²d©ÒeÊ–¯P©Jµ5k×kؤy+ïŽ]zô8dÄØ‰ÓfÏ_²bíÆm»•/Q»a}fôõÛ·ïÞjEýä¡lfû¶®Z0ud¿NÍëT.áV(O6¹0£ÊâB¬Wùµ%g -5›vè;bêÂÕÛœòñ‹ÐÆ´ zð@>‡-_’³yíï‹æL›0jè€ÞÝ:¶mÙ¤AšÕ«T([ºD1mëg'GGKœ]ÝLîE=Š—*S®b•ê^µë7nÖÊ»S·Þý# -mÁ²Uë·îÚôÔùËÚk<,íìmX3+³ë–Ì;¨[ëúÕJ›çÓf,ÿc},—.äie–\v.Å+ÕiÑyÀèKÖï:röÊamÌRbÚóŠòÕ8òÅ…‹çL?rp¿½[6©_Û³jŲ¥Š1»jûBÙæÏ—7OîܹõíïÄ?äÉ›7_þv–í¥Šê´xÕiФeÛ]{õ<bÜä™ó–¬\¿u÷Ácg.^•íL{)ÑkùŠmÙÌܺvþØÞM¿Ï8´§w#ÏrEmsdN›‚ë²±>ÖKò´2wA·’UêµêöÛ¸ÙË7í9~áúGâ¤R{šòÏ?%vëºÏ™cûwnZ-_&=jpßîÛ4oXdzj…2%<´-z -äÏ›;§¶bæL™Â7óÌ”)s¹cŽœ¹òˆb+TXÛjªdÙŠU<EKkѶc·Þ‡™4cÞÒU¶ï=|òÜeßÛ÷ýõ©ùæµ|Bó©ÿ]_Ÿ“¶^8uDßMkŠ…Y\YXþÇúX_t‘,•8´7•ªÖ M!çܲÿ¤6*µË¯ò¢•¼4ú,àþkOÙ»mýŠE³&Ò¯GÇ6ÍÔª^©lI³‹£½e[»LÒËÍ:S§J¾+qªT–ýd3dÌ”%kö¢Ôlvr5-Q¦B•µë7iÕ®KÏCÇLš¹`ùšÍ»;ãsMLÍ'Ï_h¯ÚÐÖ‚÷o^9{xçú%3ÆèÜB,Ì\æ–ËÿÄ,ÌbqÂ.¥Î=oas™ê
½{
›¼`õöCg.ß´ŽJÄB_ZJlÏÖµËçOŸ0b |ëZƒZÕ*–)îîêh_ _îœÚ¶‰éô`å×I"ĺ3¶Ü½8]úŒrûÏ\yDOsp1ÉB«êU·Qïν;eÎâ•¶ï;rúâµ›Ö2{õʲþ?lϦå³ÇÿÖu½ª¥LÚòŸ“ÌXœ°ËcòÒ…ƒ{YÏÆíûŒ˜ºhÝÎ#ç®Þ¶ŽJ¹Ó/V]÷%¶eÍÒ¹SÇí×}ËFµ«W,SÌìâP0Q`™ô
`“'µ¥mÖÿËûŸñ‹¾Çÿ¯Ú>Ù)´Œ3f=-w>Yhf’å*Ëí5ÚuíóÛȉ3,_§½ -÷jx™É× -æÖªŒuYì‰uÍ/k¬[©j
¼{
›²píÎ#ç®ÝÑN+ßj¯å«±›WÎݳyåü©£v÷nR»JIJeJaïà¿ý#ÞÂ[Zx¥J“>³¾Z)Qf-;ö<vúÂU›÷=«½üù‹à`ù’Ù[WÎÞ!N2‡õònP”[!Ye¬þcOÂÎ+e™JWkèÝ{ÄÔÅëv=ý®¼Ê/kìuè‹gÚó8·¯]2süùFŽj効%–1]ªvAÿž‡õÃ:Ó6ØÎ”-WþBÎE´=8:÷6aÖ’µÛœÔ›Y¨2íºì¹#»Ö-š:¢·wÃj¥M²Ê8ÇŒ=ѯÁŠóJ±s+]½Q»>#§/Y¿ûØß{ŸË“/|’× -ÎݳiżÉ#ûw‘oI+ar°ÍMÛa'Š?Ï!bÉ -H‘*]Æl9ór)RºrÆmº9eþŠM²™É·L‰föâ¹<ɼpl÷ú%ÓGöiרzi7±.ç˜úUÙïÿ‰È÷E<žÚuþ´™E•ª¦ÕØÒ{O\ô»VcÁrq}úÐŽuKfŽÜ³]ÓÚ•K¹;Y7q%ååVg¢%J,wÚ묒ܾQ¦jfí{7S4³ƒ§||Åifà‹¢ÊäIæñ=–jU&&fÁ\™Ój×þie†G»x!ŸKÊ”ÓεTµ†íúŒš!kÌç†véâÍ[}TŠ>q|ß–•ó§Œè×¹eýêå<\ -åÍaÙŽîçèùPšðÏ Ó>3 •¶Ãca·båkÔoչ߈ÉóWl–¯£¼õ@ŒLQeÏüïùùœØ»qéŒQ}Ú5¬VÊÕ.g¦4¢Ê¸axôd‰“¥Î”£€Kɪ
¼ûŒœ±lÓ¾“—n>x*/½}óúeÐSí²ú®
Ëfm¬IJ%Ť̙UÛX3¼Ä¢å§Óöx´–™X›å.àh.Y©V“v¢™ÍZº^{E¸62_=¸Ãçä¾MKgŒìãÝ jI—92¥N–˜ËeF'¬Æ2ÚØ:¯Rß»·˜•›öº|S^³ÔØ“ò -Áö5‹¦êßE´±²Eí,[7%ŠÎ³ü„ÖÏqÒÊ,³MÞ‚ÎEËV«ß²sÿ‘S¬Þzà¤ß]y–ùB^ʸqIVÙô‘½½ëW)îlk“‘*3<Öd©2dÏïT¬r½6½Fˆõ˜¨1Ë%XýÊÅ=í¥+æMÖ»}ÓZ•J˜ -ç¸ ]t¿Ú9B™%M‘:}–œù›JÈfÖkèÄ9¿oÚ«½žÒRenŠ*Û¸túˆ^mêU.æ”?{†T–ËeÑù’/Åz,}¶|E+ÖiÝcØÔ%÷ž5¦]‚Õj,à®ï…ã{6.›5î·îmÖ('ÚXîlÓè{ÇÌF'> EÛ|;·hfÕho¢Zº~÷Ñsò
.ò$S¯²½–LÖ£uŠEòeKo½\Ý?$ùtôË$)ÒeÍS¸HùÚ-»²hýÞ—n†×X ÿÝëçîZ¿dúèþ[ÔRÊ,ÚXfqN³»œ[ÊLÛYÌÌ¢™•Ôß:mÑÚò™‰°*»ásbÏúES†voY»|‘Ây²¦Ó.d00
Ь1qb™"m–Ü…Ìek6ï:xÒÂu»Oøˆõ˜¥Æ‚ýï\Ó/¥èÓ¾IÍ -Å\ -æÉnic1ûy
?X7•Û¼‹fVÐ¥Xy¯Æí¬/ݽrËZebõ|÷º…“wm^³¬¹Pî,i9Å4.ú¢ÿ×äi2ç*h*ãÙ´ó ó×ì:vñÆëzLÔØí«gm_=ÒОÞ
k”•ïÕÎ"ßEkÄþæ>°BÛÍÊѽLuýM–gòUæwñØ®5ó'êÜÔ³´[Ay!ƒÅ¿Q‰pñµTõÆŒ›»zÇÑ~ž„ÕØ£ÛWÏܶR{yCýª¥´77†í:óZØGï$M‘6Sv¹ÙP•º´×$m=pÚReA÷|ÏÙ±jî¸U“2´SL–eF$ìÄÒÆÖ¹DÕíúŽ™½rÛáó¾÷ži×.^?{tûÊ™ƒ[W̯½P«„«þ6m?¦!¼™iÛ¦t)^I¾ºrÜì[œ¾¬UÙù³ëço[1{Lßv
ª;Ť•Åx´E¿~béX¬R½¶½GÎ\¾õàÙëwë×`õ;}`ËŠÙãê/9µË•%là £±°f–,¥™œ<*ÔlÖiÀØÙ¿¿Web!ypËò#{·©[ÉÃ!o6¹øg`Æx,Ï&é'–ê´ê9|ÚÒÍÎ\½ð\>—^c¿Ï; S³šå=œä;´Ãß;kÔãemfÚÈ´ÉïX¤œWÓNýÇÌ’U¦OLùl¹øÑ÷oZ:mXqŠén¯-þY–Åxô™\ôËË݆LY¼aß©+·ýµ×öD¨±YcúwlêUNP¬ØÀrš©m;”=Ÿƒ{9¯&?¨²'o]>¹wâɃ»6ó*c‹–e1Ÿ~ÐdÅ¢¿t
qb9qÁº='.Ýzô,øÕ›7ÔXϲîï¿¡ÑØÇÊúñò]ÈÙòv/ëi²3Wn?|ªUÙy!cí‚ ;5®^ÒÅÖ†eYŒG¿B&dÙó‹EÃýÇÍ[³ó˜Ï
yìõ5ÖA«±¼±êÙáû)¤Ï–ÇÞ¬WÙì[ž¹z[>Ã$ŸÆô»ptçª9cû‰Å1Ç|,Ëb<–+di³äq(*ý}FÏZ¹ýÈ¿ûO^ˆ{]³ÔX?QceÌöy²i›LÄšLÂf©ÒgUVFVÙØÙ+¶:{íŽÿ³ òB†<Åü}æÈ^mêT(¢-Ëô«eFÿè %Ú°”WÈÄ‚¬\–=†O_¶åà¹ë÷䉥¥Æ®ž±ÔXã²Æ²¦O¥-ùcͦLá{\iU&zY§ãæ¬Ü~øÜõ»²Êä)æ™›—NÚEͲ&»œS30c0²
ü[Ë6¶®¥j4í2xòâûOk'–¯D…úß¹zæ >+5–2vÕXÄÝúô*ç˜ÇÏÓ®&ß—¯b’§˜§ö®_8qP§&ÕK:çÏž>%3æb–鲿u,V¥a‡ãç¯Ý}â²~b©½îB^fÚºb¶\…ÕX,8|??„½!YV™8ÇlÖå·‰Öî:._Ó«ŸbÞô9¾kõ\±,«_ÉÃ!OØu£ò„ý2¬6,Ý+ÔiÓ{Ôì•;Ž^”¯¼xõúÍ«—Ïß½~öж³ÇÊóJÆÂß« ˆUVؽ\Í]‡L^´^¾RéÑSù‚l±ø?D.Ëz¶ª]Î,¯c00c*ÚeX9,¸–ölÞmØ´e[ó½ÿT[ô¿z|Ï÷üáí+çŒðqý£ð*˖סH…Úz›¶t“¼¤ì(Ÿ+¸+–e›–LÒ¥iR.¶aÓèŸ;þG®úN,Î,å°lÔqàÄ…ö¾zW_ô‡¾xò@œûïX=wüÀNM=ËÆê¯²TòÉ1JuÛô9ó÷‡ÏûÞ,_‘!/ĜܳnþøþíT–3¼$‘x}X&M•1gAsù:múŒž³z×ñK·´ÙëÐàgÚBF{¡L3¯²îÖc±ò¡±¼GAýxñ*
Úõ;gõÎcò%qAÁÁAOÞ¼xlÇÊY#{µªUÎd—#c*Öþ1}ÕŸ"]¶ü.¥j4ÃrùÖÃü<}-O,ݺ|rÏzý%åÜÇò{ï0\JVoÜiÐÄ…ë÷žº"fH°X–ùž;¸y©˜Mª—p’—dYûG¬«þÌ¹Ü ý€ bXž¹¦½¼çµXôܹzzÿÆ%Ö/çÍËk,¼ÊÒdÊYÐTÆ«y·¡S—nÖ.ú= ¿Ð©½ëæë×®^Å"ö¹XûÇDô_¤LoSÀlV½FÍ^µóø¥ÛþÏC^½–o~»wýìÁ-˦èÙºNEù6ŒT±¼ÆÂ^D.™¹íÝË×iÝkÔ,ýlù鋱,»éslÇŠ™#z´ð*í*Öþ)~eíÍÑ™8ì³äq,^µqçÁS–n9t^KQc¡ÁOܸpdûŠY£ûx×·¾¡,ö¿rY^û×ß#NeDwî?~þº=b`‡„=‘sÓâIƒ:6¬âá;sšd´²hŽõòE;Sù:mûŽ·nï)yfi]]‹þyãú·oXµDœyklØîj²ÛŠeYÓ.C¦,Ó÷þ“ —Ábpåäî5sF÷n]»¬[›)ieÑ›°F–שDõ¦]‡Mÿ}ûQíÌRËÇw¯žÞ·aѤß:7©QÊÕ.μÉ_ß'R>+–eåjµê9RÌc>·=ÓæCÑž·.›:¤s“ªÅåeZY´&¬‘4W¨ëÝüÂ
ûÏúÞ—¯ïц¥ï¹C[–MÖ½…|/Y®ÌÚv%qáñø!üU%…=*Õo×ü‚õ{嵿ç!/C‚ß»vzŸXû÷m[§œÉŽV͉ØÈj4ë6|æÊÇ/ßzi–b‰¼rö¨ÞmêV(R8O–´)´‹‡þbò$)ÒgËï¬
̩˶È3O_¼|è/~±í¿OÖµi5k+£È¢-Y½vý',ÚxðœeÕ¯
Ky²?~@û†UŠ;jïï—.ì.1ç³vëÞòó‰ËwD+{ùâé}ß³û7,߯mò¢•‰ÌŸÿõcœøµâbôkdúЬYwÙÈN\¹£¯úµa©_¶Ôžç³ Û©$n<ú²,¹8mvð¨Ü°ÃÀI‹6О}ù2äyÀíKÇw¬˜1¬k“jÅòdN4ó2º¢=kùkÊ6væòbE6aѦƒçýÊUÿ«—Ïýo]²<cyÅ‚åqä±°Ì”éÅfiÏæâZ±ãØ¥ÛþÁ²•=ð={@´²¾mÅ ¦mötÉy3Ú"_«˜(iêÌy‹WkÚModúå‹ÐOäêxý‚°§’ãÜ{È,Sœar¯PÏ»ŸXûï?sýþ“ðVöûô¡]Wñ(œ+S*^Œ]‘ÃωS¤Ë^À\¶ýÆ/Ü(Ù3ÙÈBÅyþá-â<_Ë8ú*RËÀL›U¿<34ìòŒ¥•í_?lŸÖ5K»äÏš6YbyÃèŸ8>F(©2å*ìQµq—¡3VˆSKmEöJ¬úï\9±kµvÅR˸øzø"\h.W§MŸ±ó×íÓ[Y¨ÖÊŽn[>õ·Ž
*¹Ì‘!å¯ÌËè‰~ý"mÖü.¥k¶î3vÁ†ý–SKýÙ‹&ìÐÐ2,ãà;{Ÿ2s(Vµqç!Ó–o“ìyH¨<Á¼~fïÚ9£z4¯QÂQ.ý™—ÑmZþ*¯_©Ô ãà©Ë·‹…q@ÐËW¯Bžûß«þ3†woîñ¤qìQ°ì¼>»|ò_Fó´VöTkeþ·.Þ²xâ -Ÿ—úù¥Y¾@6i"ùI°1y|¢£ºÆä1&?í8Uê´éÒ¥I—.mš4©S§J•*e´G|Óô™säw”¯N0qÑfq×û?ù*4D¿€´p€ö
ªw.+kÆtiR¥L‘B1u|e,SàÓ‡À/òÓäRË]±J{µì5zîº}g|„Ÿ_®’ç—ÕäItºI'ŠâcHu¼WˆÿŒâϵn˜$yÊ4é3fΚ-»LölY³fÉœ9S¦ŒÑžL™³Úä¶u,R®fË«ï>yúJ,ÉÄRE÷kƒª%ÝìóçÊž5s¦Œä6¦Ž€¯I -™ä_8ÂzvIíæ«,×奚çlX0®OëÚeMsʈJõ‡Ð€ðBÔŠðÇ(ÝŒK{ªÜ<MúÌÙsæÉgkWPÄή€mþ|yóäÉ;w®èMîÜyó(ìR´œg“Ž',Üpà¬þjÅÐà'÷®Ú³vÞ¸þ{–óp-l—?oî\9sæG@¶˜:¾"dÒéH•:M:ùÙKÚþ~–ë±ÖÕ§<¿”×å g–iSGñ¤<¬u˜HöÐkªUÍ̺-ˆ8²çÎ_ÐÁÅÍì^¤Hww“›«‹³““££ƒHáh‹øË\LEKUôjÜ®|‡µöÜxèë7¯ä“z§ö®[0ñ·®-ëU+W¢ˆ›³“Ca{ûBcîøºäGÀ½i;¸—õjÖu¨|)†þ: -åË•-SºTÉ’%DŠG[Ä_^²T™ò•kÔmÖ¾÷ð©K68ë{ÿYÈk¹_¿¼»ãâ©Ãûth^ϳJ…²¥J–(^Ì£hwsÌ_q|ñÈ-&EAGS‰JuZv6]{½|CÖ«—Ïݼphó’)ƒ»4«Y¾˜Ö´óDñ¤<´BL/ã©S&Oú«,³£èý`ÖcËd“¿°©x¹*žuê7lÔ¸q£†
ê×S»VM//OOÏÑOO¯šµë5lÞ¶K¿SäÇ
jìÍ»·¯Åáí{îüü¤!½;µnÚ n-/ÏÕ«UR¹RŘ9¾6ÅŠy|áp°tmk×^±=B×¾+ßL>wlÿŽMkU*íavqrŒêãGuèuh“-K¦éR§HúkÔí4by¿V†ìù -›KU®Ù°y›öºtíÚ¥s§ŽíÛy·mÓºU«–Ñ›VZ·mß©{ß!c¦-\³ãˆÜþ"äõ»?Þ½y(7$Û½~ÉÌñÃôìÒÁ»M«–-š7oÖ¤q£˜:¾2Õ¿|h]»t¹JÕj7nÓí· óÖÈ·Æ=GÔÛ×âwõ;hËò£úvlQß³Jù2¥JFññóå# ¬ílóåΑ5“8õHUU¦¿¸>™ö¹’¥ªÖmÞ®[ŸC†
>|ØÐÁ¿
п_ß¾}¢;}ûõ4dĸ)s–®Ûqøìµ»_¼z÷矼
-¸sõÌ¡k—Ι2nÄAýûõéÝ»WÏÝ»v‰±#àkÒ¢E‹æÍš~éÿîU«NýÆ-Úu8zÆÒûO_g—¯Þ¾{ûêÅã;WNíݰxڨݼ›7ªW»¦W?Š#ÀR‡EÌnÎ…íòæÌª}æ^Ô|xµåµé²åw*V©N‹N¢ŸLœ6cÖìY³fLŸ6eò¤‰&Œ?~\4füø 'M™>{þÒÕ›w9{õ¶ÿó—oþüëï¿Þ½–[ù\:updž•‹çÍœ6yâ„ñãÆŽ3zäˆáC‡ÄÔéôé¥<ÂÛöôE¢m_x¯m_<¶kÝâã†öëѹ}Û6Q{ -–ˆ¨ˆØ’¨ÑØK4Qc7F£ÆÞ½ÅÞbï-jbŒÑ—æ÷{gvÙ™uÕ<Ïï½è:ìÝ9sçÛ2ç‰Îþ´Žä%ðxs-@Šš -¡¨ Èý¶²Žû
¶ -Šä(¿ÕÎå½ -ZJ~Ü?g%y˜éFÊ¢%Ê„ÃHÙ¸u縤áã§/ø~Ã΃§R®ß{ôôùË?±#{?yõêï?Éxy#™le5exḂõ«Ša!Å@g¦`Ì4ÿ‰x^ÈkJžŽ”=ú}2fêœïÖü°÷È™‹7¨Æþø;²÷:^¾ø•$Ný¸eùœICbZ7Œ4Ê–,ßÇ+9Ý7S™™‰”+mMYM)GNžùíŠ;œ8O¯·hEö^"¯/~pãÂñ½—Ξ8¬_×vMjEða!ºÂr"¦£1Óò½…§·_Á"t¤¬Ý¸M—¸ÃÇO›¿lݶ}GÏ\¼~ç!}L5öþbîÊhâÔî
KgOþ,飨õ«™˜r¥‚ -‘13Wæc¦<T梶*°¦ä+ÖhØ -FÊOÉH¹z˞ç’¯Þºÿè—g¿£ÆÞkä¯Êž?ýéΕ³Gvo\6wê¨Á½cÚ6©]Y/S¢HA?Ï<¹2ëÌdyäõ…n¬¬AªV·9]SšGÊãç.߸K28äGŸ_¡ÈÞ[ä®ì·ŸÞ•íÙ¼â›éã‡õÿ¨S«Õ+råCtþ¾i -Ò.?Àt%Ä%¤]~€éJˆK(I»´IWZ‹éJˆ -”¤]bºâJÒ.1] q %i—vD†ù#ÊQ’v™>])i,æÄ!ªP’v™.] sâµ(I»D‘!.¡$íÒ"2{9qo;~ä_€’´ËÔt%L¼D´ $í/—P’v‰‰—ˆK(I»ÄÄKÄ%”Š/ÍX‹Œ¤]:&^"Ú°Éíµ“‡‰—ˆK(&^"ÚQ,2L¼D´¢$/—P.2L¼D4¢Ä¥ -9âÊ\ -Pdˆ(s)@ŸÄ”¹ OâÊ\ -Ч -m¨ú ªQæ·‚>ˆ¨ú P%2ô)@´ ÌÔ} -P*2ô)@4£Ì9êô)@4£Ð9êô)@4£Ð9êô)@4£Ô9 -SÈÍ(uŽB‘!šQê…>ˆf”:GÙf÷¢È…(uŽB‘!šQê…"C4£Ô9 -E†hF©sŠÑŒRç(´õA4£Ô9 -E†hF©sŠÑŒRç(ôŽB4£Ô9 -E†hF©s”!šQê…eˆf”:G¡A¢¥¦>hP†hFÈРрRç(4(C4£Ô -
Êͨ”!ªQ!24(C´¡JdhP†hA½ÈÐ Q‰:‘¡A¢µ"Cƒ2D5*E†eˆzÔˆ
ÊM(5Zü -DÖ%iÜ<¢m"cª6í2`ü¼µ ²û(2Ä DæHD3`üüµ{O]½ÿŠqŒz‘yú³ÕšÅœ0ÝÞÓDd ÈG¨YY.OßÂÁldó®ƒ&.X·ïôµû¿üŽ"C¢RdY³çòô+ÂUoÑmð¤oÖï?síŠq‚z‘yù)Í×hÙýãÉ7üxöúƒ§¿ÿñ7Šq€‘å/ZF¨ÙªÇ'Sm:pîÆÃ§/PdˆCT‹,wÞAeÅZ{ºxóÁó7þúâOâM"+'Õi7ì«%[%ßü‰ˆìŠÉµ"Ë‘ÛÛ¿X¨©n»øÏ¦-ÝzøÂGÏPdˆcÔ‰ì?Ysäñöו¯P¯}ï3¾ßv$åö£g/QdˆC4ˆ¬`ñ°Šõ;ôùõòíG/Þ~Œ"Cœ ^d> ²J
:&Œš¹bDZ‹wÿ†"C£RdÙ@d%ô•Fõ=kåÎã—î>‘ýƒ"C ZdDd:%Žž½j×‰Ë 2òûq’9šDÑ(:qÌYdÏQdˆc´ˆŒä‘D÷;gõî“—ÍÏ,¢ÈÌQ+²œùì‰ìm_ò.“QdUNE&gÄõ;D†O_#αٸ¤ÎMPdÈkG³ÈHÚ%ŠQ‚•ÈæKê"+…"C^/²ÈîƒÈÖÎ? KÓªŠyÍP‘ýrÿꩽkçCDè«Td˜v‰(DöÙé½ëæOÛ¬èë‰"C^#Dd¿ÿrÿÚé}ëLÔµy$\Ø×3ˆÌŽÊPdˆd‘=¸vfÿúo&
îÖ¢:RØÏ3Wö¬(2äuñꟿÿøýéƒëgܰpòÇÝ[ÖàKñóB‘!¯"²OÞ8w`Ó¢)ŸôhUS(S4¿3‘¡=¢ÙŸ/~}xãüÁÍ‹§íÙº–X6¨@ÞÜÎD†ÎQˆr^½""ûéfò¡-K¾×¦ŽTE†¼^¨Èž=ºuáðÖ¥Ó>‹oW×ZÌß;wG"C{2DDd/Ÿ=ºrdÛ÷3Fôn_¯By¿wžYí}Q†ödˆd‘=¾}ñèöå_ìÛ¡~ŰâŠíÉ•P‘ýöøÎÅc;V̕бA%™‘¡=¢–W¯þ‘=¹{éøÎ•³F÷‹jXI_" -3´'C´¡RdhO†¨GƒÈОQ‡z‘¡=¢u"³$+¡=¢m"Cç(DDf@‘!ªPaµh“¬„"C¡Â3âm¨fÄ!*Qaµ˜!#n
fÄ!ÊPaµˆqˆ6TX-bF¢
åV‹˜‡hD¹Õ"fÄ!QnµˆqˆF”[-bF¢åV‹˜‡hD¹Õ"fÄ!QnµˆqˆF”[-bF¢åV‹˜‡hD¹Õ"fÄ!Qnµ˜šqˆ*”[-Z%+aF¢‹Õ⥻V:¶Z¤"£ÛÜ[eÄÈp¸Dœ -J3¼ÞhâtœÉ¤gD$œUäôÓ8ÁH‚!ïbõ„ÈÁ†‡kä½(pý4V‚ðXJ9½(käõ“ -“à“L+… YP½ÕUÄÙ¿q©c…ÍŒÁ¶Oèe¿Oˆ³Û'Ä)î’ìö qjú;½{œýÞ=Îî•¥VƒžáyøtøzRŽÊ®Þ -•£'j¦ÇÒ^¼ HëK¾ÒÅÓkàœ\a¶Lc -p
F“Q®#ŠæÓBG&‰F¹þ8É$š?Ÿ%‚Ïx˜þC¢ÂC1šÌ‡¡5ØÈÈN äpdg¹ciÝ+6F×-!:¦Gl|"ëÊRÍ™dÙÉò“ÿŒì… ò Ðirz'HDrÈàÄ#f¼žÖ½RËr‚¤ƒº‡Ð¡ïÍ´´‘%‡7ê
¤â#»h?u¤ÚSÓÒ–Ú¢g–cd¤yjo€¶ÄðFRPä¡G^p‰v¯"'˜¨Vpk;/äv]&cHßŲpN‰57dèf}F³>à¨É ™Û'Xî-銣)ãaÛ¶È2“yÚ›&>ݸÆd×…ŒŒo¢‘#•ËÃM§µ}‘¶qžÎ’ào æVä'0¼¥ÑCwh`+ôÊÐp 岜”±=Y7³Zk€‘!µ^ìÕ -]†dâD‘|€< ×3Iæ––Êáx9HXOÈC*9£
4èFÈèjçTƒÌ—C®T4 -ôJa(gaJ!ßWÍy‘F3(|©9¸z>–)òé`…#õ,,SÈe³0£È˜o„ÛÂ@g7È¢öצGƒÛË -æ‹g²ŽÂuÑ ’ -†Ë‚¥<OÁ,€¬ËaYr¢óNÒûð©z`:ÅËG
dÐ!‘È
9/Ë*F0·\Aâä¢ ²h(àÆÈêh;†HXVÖ/ÐàÈ—QJ-KgÒäâ ,x'OèᙜÀ EIûÚ„aY!ТµÌ³¢#Á<e£_+Ò¯(kõˆ‹`ùGdl·ñ©ßXÂ4 -£Â¨0*Œ -£Â¨0*Œ -£Â¨0*Œ -£zGB¨0*Œ -£Â¨ÞŸ¨ÞnHF…QaTFõv£z»eFõ?ÕÛ -ÌqHïlTo>0%!½á¨†ô†SÕ› LmHîL[<nÍõ^ol¯1#tk$ï9oÅŸQùmŠM’Íï•Þ³:÷î¦ïÏj½ÿ€Kù,òæ+ÙsäÌ%o‹ál§yx{O²[WÞ€#§ý
8äÀû=óúøúå/àÐ}aòz汿/œ -äöÌ›/¿¡ÂEƒŠAE -øûåËkw»¢,t÷ÈÜžÞ~ƒŠ—*]¶\hh9²‹VP`A?»»hÑ}r{úä(Z¢th8Im—Œg(_¦dP¡ü>P$ÃændëÀì¹<¼ó -*j+V©^³víZ5ªEHlXˆ.°€'Ý¡7‹u r -/߀ àò|ÅÈ:šµüðÃV-šÔ¯YÅh(S<0¿·GÎôûÒ«ðð.P¸d(Q«Q«ö»vïÞ-¶S»
ªW`Êè -™·Î’®D.Ï|ÅÊ0k5iÛ¥gߤAƒ%õý¨s›Æ5*„‡M]ÓoNv@Ìíå(o~ÛgаQcÇ=|pß®íGËÓ½†Ã¢{&æÉ›¿HpxÅ:-;÷2jÒ´™³gMŸ2vh¿®mêWfK-àÇzwb¹„w¢¥Ù*
Ûõøù³.Y¶tñüéã‡öíܼ¦‘ìÌî‘nÓls‰ 2|µ&Q½?8sÑÊõ7®[¾pƸ!=ÛÕ/èë™ËúBÒJD6ëœ0bê‚›¶ïÞ½cÓÊo¾Ñ¯sÓj\é¢ùóæN_"kŽÜy%%º$Žš¾xÝö}‡ý¸kÓ²ÙãuoUËX®˜¿wîÖ—N6Ї+/ÍA‰þ£¿^ºqÏáã'رfᔡ½ÚÕ«P¾xAŸ—÷Ã˯p0[µIçÄQ_/Û¼ïØé³§îÝ´dúȾQ
#ÂK˜/ÝZ%9=}K*7ê”ðùŒ¥›÷Ÿ8—|þäËgéß¹I†\ºu ré9=|Jè+ÖïÐû³iK6í?q>å™Ã;WÏ?0¶Y$RØ*˦zýu¡¦:mã†N]´q߉óSÎÛ½ö›ICºµ¬Á—.âç•¡zÉ¥*+Öü°ÇÇ“®ÛsìÜÅ‹çïÝðíŸ|ôaM±LÑüJÈ—^$„‹lÞuЄùkv=›r1ùľ‹¿×¦¶±\Pô7„^¹t*+i윕;Ÿ'ܼä«áñíêšBå’¡„G¾B%Â#Fõ5sùÖƒ§/\L>u`ËÒé#z·‡¢ó-fMWYYsäñ)X¼<TVŸÓ—n9p*9Jü°lÆÈ¾ëW£·0c‰ÜÞþÅB¥:m{
ÿê;Z½É§n]>sTBTÃJúâéo:½tZYÆZ{~:uц½ÇÏA‰CÛVÌݯS£}‰ -6R´”( %@Xr‰‹çAŠ‹¦~Ú³u-±¬])¦•˜#—H>¹Ów_‹kSG²/^¹DcK‰‹—.œúqË’iŽÅ[2¼²¥Ä¹‹—RNüaYæâµ-q9åÌ¡ß=²o*^g%Žž»tùâÙCÛ–Ïü<¡cƒJöän[âÊ¥³‡·¯˜5ª‘{ ¹Û”8%Î
endstream
endobj
79 0 obj
<</Length 4920>>stream
-ñŽN´/wÛ—¯B‰ ÷ÄèF¤WTRâò¹£ wsQVâüÑÐ@Ì%26G%:Ûm„o¹D’éJTQ‚¶Õ*KŒSQÂÒjWï:¦´„¥gPX"÷Y³ûXòç%Ì=\ÌÀ ó×î>ž|嚣¤OÌåé#zdóØA¬Ýsâ•kW–Èjîw¡§=õÞ®^»’|lçê¹Jо½FËî0ØnØw2åÚu(±Ëq ?`xþè“)‹6î?uñÚõ«NJÈcTíÖ=‡N]¼éÇÓ—®ßPP¢X9y\²åÀ™ËP₃d¬¥ÓŒzí{6}Ù‡Î^¹qóÚ…ãNJ,£sß‘_¿íð¹+7”€©™üT"3€Y+¶I¾zó–“–8föªÇ.\»uëz --a·
Ê%@Vé¿pJ\È´DYH¹~JÛ•I;O œ‰¬Rà×@%i=œm yÐdµ`”¸vóæÕóG §¦ýnÆ^4m’²2iûõk—ÎÂøA§26=µ|ËÍ%Oœ¿†jW._€)L°úÀø‘q4°.ÑmÐø¹+·8yþBò™#»×;uX<Ì‘Ã2Ì‘SKðÕ›Ç&™¹tÓžÃ'N<ºÿ‡•ó&~üQë:Rh†q–ð"ÓðjM£F|¹põ{<°wÛÚÅ3F'Ŷ¨AÆót3÷Ôi8S¹a‡^ŸL˜µxõ¦mÛ·m^³dΤ¡½£šTåJ¡«ƒ÷Ã7°¤¾bÝ»ö1yַ߯Z½êûE³¿™Ôu½J°I?/‘Kä(*VoÒ±çÀ§Íž¿`þìi“FŒëØ´ºHV9ž9Ó• wÿ ÒLD–ÑqICGŸ4yÒøQC“â:µ¨S‰åšw›uQî¼dµ&Vß2ª{ï¤ÁŸ|òñàþ½»ulQ¯ŠJWk†AVÁ ½±jݦm:ÅvïÙó£î1Q›Ö©"†•"+Â\Ù3Ìw³å̓ׯ®´^Œ¨Q¿I‹Û´móa‹ÆõªWÂB‚|É)ÒMdHX¹`ñ¨)ÏJÕjÔ®S·NU+‰Lh© B~Þò)Ò/m³å$ëíBA%K—7p¢d2™Œ›ùnê–5½_ÁÂAŃK—
-_õ!¶Æ¶ùÞ@§Ós¸Ïqúï& -øû,èlÓÚÔï?r“ï?¼½é¤Žw •¿cÉ&Ç’'‚/Y¬¿ÇÉ‘CáÞ€æ/–,›*Úè-Ã÷Q -wíz¶`R³-ŠÒ½Pˆ©¥Hœ”eã`–3{ˆÒ= Œ|ØÄò²‡/ñŠ4©¨ÀéMº ñN¥^Û&Ž—ÊûÈ»@k{rØhq§æŸÝÎ'®ÂPÎNIV>ÊNw¿xg¯€˜/¿
XsոͦR¶#6ˆ›JÁÖ¦Òê-©6•‚å/e6•Fm*ßïίoÞ©ßv½ƒ¿|í¿¸t16wÄãblîI[`o&*U½±”ö†Czg£rØ[ éÊq`•¨ÞbHïlT™†Q)Œê‡„QaTïOTÿ–n£Â¨0*ŒêßÕ[éÝŒêßrû0*Œ -£Â¨0*Œ -£Â¨0*Œ -£Â¨0*Œ -£Â¨0*Œêê†QaTF…QaTÕ»F…Q½á¨Þb`ï`TŽCzg£z+ý{£zÃ)é
öF¥*¤7˜†¨Ü›æÜ˜‹!¹#¶×Òë -Ïñhï
£$Ô·Å{ÄÛ0<øÀÊvAÝ»•³Ø@dMu5»A8(j5ššSdK5œÈìýðöì9räÌ™‹’3'qµ0{Zdæ4š=GÎÜy<<=½¼¼<===ˆÏhb„a¯)=G®Ü^Þ>ù|ý -)V¼dpHHp©’Å‹
,˜?_^bJ’;Ө·_@‘âÁeˇ3,Ë2áåË…”,VØ®9)=…Ùi´\8oªT¥jµªU*UÙ0‹9i¯ê!™'¯_@±0¾bµÚõ5iÚ¤qƒ:5*›ØòÁÅ -å·ñc1»Äø
«Ônܲ]TtçÎÚ·nÞ f„¨)à›7OÎl}CóäÍX¢¼Pµ^‹±=ûôKLìß½sÛfu«Úó•¡Ayø,V†\·e§ž‰C†õùˆ¡ƒúöˆjQ·2_®x!ßôÞ54(/ßÀ’a¦šÍ;õ8bü”i3f|5yì°¤žQÍkU.R okT‹¥'5ýhàç_Ì\°xÉ’EógL1 G»FUù2ňjºÄ>ª ®œ±f‹˜~Ÿ}1gɪõ›6®[±hÖ¤á ]ZÔ”2z“f±XNê+ÖkÛóã ³–¬Ûºkïž[V/š1vP6u+êKJçMj± -fª4îÔwė߮پÿȱc‡öþ°rþCã;4°q3Mµvâ"›Ç$õý–}ÇΜ;wêð®õ‹§èÕ¨ŠÁN j¢$ÖlÕ}5j:“rùRòÉ·,ûzT¿èƲm¨• hš›Wí6=?òíâJvýÚ¥3‡¶-Ÿ5:ÑÚh4KZåÊn^uÛÅÿjÉæg.߸e6œ“çÈè9ivóÒÉ^›3–m=tîê;·¯%ÙA,ª¢ÙÚ`É·£xX¥û~>sùö#É×ïܽs=Α‰E•Åÿ+BöÿÚqôÂõ;÷îÀ9¶SÄŽ
l=I b—ѨSâ˜Ù+wK¹qJœ?¼5ãEZÂìÖÌœU»Ž“·¯ž;´eÉWÃ{µ#•+æŸ×ÚÜÑÚ‚¯ÿXâFK\9{`ów_íÙº¶1£åd{¼5»O\¼q÷î+g~ܸhŠ]‡U+³».ÔìîÄÅ›PâòéýNþØžufFW²='I‰›—Ní[ÿͤÁÝZTÏhÏ™ÑìnÝÞ“—h‰“{×-˜8¨ksbè ²ÊöŸôbO3»[¿ï”¹ÄžµÓÐô6£6fwûO_¾%.ž€©Æ¤¶Îgf³Tbv÷ã™+r‰ÝkæÐ…”°uW“ÝkkQ³»ÍÎ^½}Ï\b\R[»TK‰rR6qþZ²å๫wä«çŽ£«¶n²M,iŸM[úáó×îܿ稄¹A•¯P¯}oÒ<'_¿k)A¼ë2)‘jG›GºvTÖÔ•0j&i7î=¸wËQ +C½Ñ³Vî8–róÞƒû·.9,Aý‡Iƒ
ê¸ÒУiƒºxóþC%Òšà‰‹·œ–€e.1vÎj¥%d§MÒhwŸ¸%¨,ñà,¡ä:TÖ•úû¡íž«Ô•ZíªošÚ £vnÏ
2ó¾$³6ýÕmKå°„UŸxÅy ;ýî
g%2öíwn¤wX"ãøq‡”ÈÌÏÓÎuñ†Ã¶ã Œµwn;+A@©-kSnܾ}Ýi‰4ƒ\˜\WVÂ܉/©óVMjöÊG“ÓœL»¥FØqKÍÄû4«ÕŒLvd½qójæN¦i³>*÷éK8xæÒõëW’œ—í”0˽m¯a_~·iÿ©”«W/[ü º×ÖjýÑ'Ô/ý<ñΦ“Wb±nãîl6
%Ƥ-ºš0wÕöƒ§Î'Ÿ;±Ë²ŸÛ›X¦š††°Õšïþ¥›ö9yúäáÝõYŸ²÷©GÆ@Xp#tˆ6eÁªö<|hïÖU¾ ûdô>ýÀâÝ_¼¼©ö‡ÝŽ™±xÍ–»wm]·tÖøÁ=Z×1…ê -zÛ”HÝ¡Sï¡g-^¹nã†5ËæO™C¼O‹Ù±¼‡K÷+\*œx™&Ÿøõ‚ÅK—,šûÕØ{ulLvF(7wúYÈÆùtå„jÚuO:vÊô™3§OûiBì‡u+…—*œaSˆ´]!JéM5š´ïÖwððQcÇŽþlHB÷ö«C‹Ê°_Cš3i±Ò† -5·éüQŸþè×+¶}³Ú•˜ÒAVj¤k/ÃTˆ¬ß¼m§˜nݺvéØºIíÊ|h‰ÀüyÓW•Å™Ôç@`ñ2ábDz›·¢{nԈ‚‹Ìç™ÞûôƒÔõ9ч2ÆJU«×ª]«fd„D–Î~tœ¡„Å™”l’ºyV–,Ïaa#«"Þ¾þ…ŠêJ†È”„”Ô ð³]Ï›ã¢ßeЯ&‹Èf¦E°ï}úAš3)ùú#Ÿoþ²™©ìeJ¾—°9…Å™4»¼›53õñööòLõ>µ÷EŽ|ZˆlãAÜLéw2™[“¦w3Í™ö½ã¯—Ìn¦Ùd}·d](ÍÌÔ‰÷iºBZÌLߎó)Åö§¬$è
¬‰#Ö½hxjÊŠ’^bYr˜1éNb¨÷'+òzx-óPVo0Yy§ÈQïO£g#GÍCY‰Ó&)ƒy(+ôÄÿ2ŒáLzÉg³€*÷ÓwõÞ
óSYvœ\©C,#ÀµHÄÉU€WF¯F/™âËôÉh¢# ŽZ›êÕÀéxÉËBMðTááGoÔ…q’^^WŠr<Ô¸‡ê…y8g0è9Ñç$½‘s}CKêÛ`‚Ü >Pb¨ƒ>šáh0Vw>B$÷ÀæfÉâfõF“H.Ð"`’Ž5 -zÖ$˜yV¶ªeE#¼“3ɇM‚(èÈ!N¨+.«E“IþLNÀ²¬\iœ•fõNTg€Wô3%Ñ+bõ,ÇÀ;F=ÏIPiFrPcÔ©¨FtªëB*M€£õØ•àr8‹‰îȹ ’I,<océõñ$|O®Žç¡Æx>Ö "ß~1BPÑ:eàΓ‚&r«à¦P+dÖ¤—]kAÈ,µeåÌ‘jON2{[„CÞ@„“ÞCØÀ™?Q]–Ú62:b,¦?$ÇÈÙ¼ÓVµä3#;ËjoÝ+6F×-!:¦Gl|¢ŽËÒ~Í$÷wr¿'ÿÙ
Ê„ i#zÑÄ5€%Ѩ“ÄÞ€±Ü^K{¥æI'9£˜yi¸ -‰ƒÎÄ(ò¢.²‹Õ¹:RcÄÙWÙ¹#UŸ›(É ‹‡ž¹+í"#ùGcÃÿHïŸn(䬆BžáÞò&"@QÏ1¢<QàEb“NÈ‹0Žó¼®å¨Q°9lûä0k}6Ë^Y“=2¦"Ðá@–wó&¦-“ŒŽgò!$n£@TEÞ -ñ±¢|µÐ? ЖAã,Ç“C"©!£ÎNUUÏ<’LnC¦C -rûáY¸"^dá"ŒÔ‰‘ŒO¶ÅáÂyè’èdÂXæ]ZO©âä‚Àè餈'Ctpï]r(%MÈÎHIñС™ó0Ã’ca^ “`¸ãd%o0Ò‚)'Á<´:ÝÁa:¿0F!¦wòpjuæ—ù -\2œVh·Ë<.™ƒuƒäB3ay²‘¬D¡{1±t¡¢‡q¦G¤ë2±d›#Y‘+3ÁJW€ârC‘Œ˜n‰,C¿X$+?r›ò&ê‰.OŒdê‹wsCÑxòH5'gá¶ðt<ƒE9”g¤÷µ8–Ž’Ö’ñ»ç´5%O5€·›¿{o=(6!,Ì+8¸It·Ø Ñ=âb¼ºõ‹NŠÕEÇÇ÷NŒNŒí?ðbû%öNˆÕõëÞ{ -0000000016 00000 n
-0000000144 00000 n
-0000044293 00000 n
-0000000000 00000 f
-0000122924 00000 n
-0000270588 00000 n
-0000044344 00000 n
-0000044851 00000 n
-0000047150 00000 n
-0000123337 00000 n
-0000052618 00000 n
-0000123110 00000 n
-0000123224 00000 n
-0000120651 00000 n
-0000120794 00000 n
-0000120937 00000 n
-0000121080 00000 n
-0000121223 00000 n
-0000121366 00000 n
-0000048770 00000 n
-0000049060 00000 n
-0000049355 00000 n
-0000047211 00000 n
-0000270553 00000 n
-0000048209 00000 n
-0000048257 00000 n
-0000064648 00000 n
-0000050071 00000 n
-0000093545 00000 n
-0000064711 00000 n
-0000058513 00000 n
-0000061516 00000 n
-0000058576 00000 n
-0000049650 00000 n
-0000055355 00000 n
-0000049713 00000 n
-0000050114 00000 n
-0000052653 00000 n
-0000052707 00000 n
-0000055469 00000 n
-0000055532 00000 n
-0000055562 00000 n
-0000055824 00000 n
-0000058401 00000 n
-0000055897 00000 n
-0000058926 00000 n
-0000061630 00000 n
-0000061693 00000 n
-0000061723 00000 n
-0000061985 00000 n
-0000062058 00000 n
-0000066976 00000 n
-0000093659 00000 n
-0000093722 00000 n
-0000093752 00000 n
-0000094009 00000 n
-0000094082 00000 n
-0000121509 00000 n
-0000122705 00000 n
-0000122389 00000 n
-0000122180 00000 n
-0000121970 00000 n
-0000121757 00000 n
-0000121544 00000 n
-0000121639 00000 n
-0000121852 00000 n
-0000122065 00000 n
-0000122275 00000 n
-0000122506 00000 n
-0000122615 00000 n
-0000122800 00000 n
-0000122994 00000 n
-0000123025 00000 n
-0000123411 00000 n
-0000123629 00000 n
-0000124612 00000 n
-0000134406 00000 n
-0000199994 00000 n
-0000265582 00000 n
-0000270611 00000 n
-trailer
<</Size 81/Root 1 0 R/Info 80 0 R/ID[<52D8D36F9F0F4093A63E37ACF2897B45><5FF1FD3406B84938948F332CEEC035D6>]>>
startxref
270795
%%EOF
\ No newline at end of file diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java index a40085b..57e0f27 100644 --- a/graphics/java/android/graphics/Camera.java +++ b/graphics/java/android/graphics/Camera.java @@ -154,7 +154,7 @@ public class Camera { getMatrix(mMatrix); canvas.concat(mMatrix); } else { - nativeApplyToCanvas(canvas.getNativeCanvas()); + nativeApplyToCanvas(canvas.getNativeCanvasWrapper()); } } diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index d4ea7a9..bd868f2 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -39,11 +39,11 @@ import javax.microedition.khronos.opengles.GL; public class Canvas { // assigned in constructors or setBitmap, freed in finalizer - private long mNativeCanvas; + private long mNativeCanvasWrapper; /** @hide */ - public long getNativeCanvas() { - return mNativeCanvas; + public long getNativeCanvasWrapper() { + return mNativeCanvasWrapper; } // may be null @@ -88,10 +88,10 @@ public class Canvas { private final CanvasFinalizer mFinalizer; private static final class CanvasFinalizer { - private long mNativeCanvas; + private long mNativeCanvasWrapper; public CanvasFinalizer(long nativeCanvas) { - mNativeCanvas = nativeCanvas; + mNativeCanvasWrapper = nativeCanvas; } @Override @@ -104,9 +104,9 @@ public class Canvas { } public void dispose() { - if (mNativeCanvas != 0) { - finalizer(mNativeCanvas); - mNativeCanvas = 0; + if (mNativeCanvasWrapper != 0) { + finalizer(mNativeCanvasWrapper); + mNativeCanvasWrapper = 0; } } } @@ -120,8 +120,8 @@ public class Canvas { public Canvas() { if (!isHardwareAccelerated()) { // 0 means no native bitmap - mNativeCanvas = initRaster(0); - mFinalizer = new CanvasFinalizer(mNativeCanvas); + mNativeCanvasWrapper = initRaster(0); + mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper); } else { mFinalizer = null; } @@ -141,8 +141,8 @@ public class Canvas { throw new IllegalStateException("Immutable bitmap passed to Canvas constructor"); } throwIfCannotDraw(bitmap); - mNativeCanvas = initRaster(bitmap.ni()); - mFinalizer = new CanvasFinalizer(mNativeCanvas); + mNativeCanvasWrapper = initRaster(bitmap.ni()); + mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper); mBitmap = bitmap; mDensity = bitmap.mDensity; } @@ -152,26 +152,12 @@ public class Canvas { if (nativeCanvas == 0) { throw new IllegalStateException(); } - mNativeCanvas = nativeCanvas; - mFinalizer = new CanvasFinalizer(mNativeCanvas); + mNativeCanvasWrapper = initCanvas(nativeCanvas); + mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper); mDensity = Bitmap.getDefaultDensity(); } /** - * Replace existing canvas while ensuring that the swap has occurred before - * the previous native canvas is unreferenced. - */ - private void safeCanvasSwap(long nativeCanvas, boolean copyState) { - final long oldCanvas = mNativeCanvas; - mNativeCanvas = nativeCanvas; - mFinalizer.mNativeCanvas = nativeCanvas; - if (copyState) { - copyNativeCanvasState(oldCanvas, mNativeCanvas); - } - finalizer(oldCanvas); - } - - /** * Returns null. * * @deprecated This method is not supported and should not be invoked. @@ -212,7 +198,7 @@ public class Canvas { } if (bitmap == null) { - safeCanvasSwap(initRaster(0), false); + native_setBitmap(mNativeCanvasWrapper, 0, false); mDensity = Bitmap.DENSITY_NONE; } else { if (!bitmap.isMutable()) { @@ -220,7 +206,7 @@ public class Canvas { } throwIfCannotDraw(bitmap); - safeCanvasSwap(initRaster(bitmap.ni()), true); + native_setBitmap(mNativeCanvasWrapper, bitmap.ni(), true); mDensity = bitmap.mDensity; } @@ -228,6 +214,13 @@ public class Canvas { } /** + * setBitmap() variant for native callers with a raw bitmap handle. + */ + private void setNativeBitmap(long bitmapHandle) { + native_setBitmap(mNativeCanvasWrapper, bitmapHandle, false); + } + + /** * Set the viewport dimensions if this canvas is GL based. If it is not, * this method is ignored and no exception is thrown. * @@ -382,7 +375,7 @@ public class Canvas { * @return value to pass to restoreToCount() to balance this save() */ public int saveLayer(RectF bounds, Paint paint, int saveFlags) { - return native_saveLayer(mNativeCanvas, bounds, + return native_saveLayer(mNativeCanvasWrapper, bounds, paint != null ? paint.mNativePaint : 0, saveFlags); } @@ -399,7 +392,7 @@ public class Canvas { */ public int saveLayer(float left, float top, float right, float bottom, Paint paint, int saveFlags) { - return native_saveLayer(mNativeCanvas, left, top, right, bottom, + return native_saveLayer(mNativeCanvasWrapper, left, top, right, bottom, paint != null ? paint.mNativePaint : 0, saveFlags); } @@ -429,7 +422,7 @@ public class Canvas { */ public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) { alpha = Math.min(255, Math.max(0, alpha)); - return native_saveLayerAlpha(mNativeCanvas, bounds, alpha, saveFlags); + return native_saveLayerAlpha(mNativeCanvasWrapper, bounds, alpha, saveFlags); } /** @@ -444,7 +437,7 @@ public class Canvas { */ public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, int saveFlags) { - return native_saveLayerAlpha(mNativeCanvas, left, top, right, bottom, + return native_saveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom, alpha, saveFlags); } @@ -548,7 +541,7 @@ public class Canvas { * @param matrix The matrix to preconcatenate with the current matrix */ public void concat(Matrix matrix) { - if (matrix != null) native_concat(mNativeCanvas, matrix.native_instance); + if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance); } /** @@ -565,7 +558,7 @@ public class Canvas { * @see #concat(Matrix) */ public void setMatrix(Matrix matrix) { - native_setMatrix(mNativeCanvas, + native_setMatrix(mNativeCanvasWrapper, matrix == null ? 0 : matrix.native_instance); } @@ -575,7 +568,7 @@ public class Canvas { */ @Deprecated public void getMatrix(Matrix ctm) { - native_getCTM(mNativeCanvas, ctm.native_instance); + native_getCTM(mNativeCanvasWrapper, ctm.native_instance); } /** @@ -598,7 +591,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(RectF rect, Region.Op op) { - return native_clipRect(mNativeCanvas, rect.left, rect.top, rect.right, rect.bottom, + return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt); } @@ -611,7 +604,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(Rect rect, Region.Op op) { - return native_clipRect(mNativeCanvas, rect.left, rect.top, rect.right, rect.bottom, + return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt); } @@ -649,7 +642,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) { - return native_clipRect(mNativeCanvas, left, top, right, bottom, op.nativeInt); + return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt); } /** @@ -690,7 +683,7 @@ public class Canvas { * @return true if the resulting is non-empty */ public boolean clipPath(Path path, Region.Op op) { - return native_clipPath(mNativeCanvas, path.ni(), op.nativeInt); + return native_clipPath(mNativeCanvasWrapper, path.ni(), op.nativeInt); } /** @@ -713,9 +706,12 @@ public class Canvas { * @param region The region to operate on the current clip, based on op * @param op How the clip is modified * @return true if the resulting is non-empty + * + * @deprecated Unlike all other clip calls this API does not respect the + * current matrix. Use {@link #clipRect(Rect)} as an alternative. */ public boolean clipRegion(Region region, Region.Op op) { - return native_clipRegion(mNativeCanvas, region.ni(), op.nativeInt); + return native_clipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt); } /** @@ -727,6 +723,9 @@ public class Canvas { * * @param region The region to operate on the current clip, based on op * @return true if the resulting is non-empty + * + * @deprecated Unlike all other clip calls this API does not respect the + * current matrix. Use {@link #clipRect(Rect)} as an alternative. */ public boolean clipRegion(Region region) { return clipRegion(region, Region.Op.INTERSECT); @@ -742,7 +741,7 @@ public class Canvas { nativeFilter = filter.mNativeInt; } mDrawFilter = filter; - nativeSetDrawFilter(mNativeCanvas, nativeFilter); + nativeSetDrawFilter(mNativeCanvasWrapper, nativeFilter); } public enum EdgeType { @@ -781,7 +780,7 @@ public class Canvas { * does not intersect with the canvas' clip */ public boolean quickReject(RectF rect, EdgeType type) { - return native_quickReject(mNativeCanvas, rect); + return native_quickReject(mNativeCanvasWrapper, rect); } /** @@ -800,7 +799,7 @@ public class Canvas { * does not intersect with the canvas' clip */ public boolean quickReject(Path path, EdgeType type) { - return native_quickReject(mNativeCanvas, path.ni()); + return native_quickReject(mNativeCanvasWrapper, path.ni()); } /** @@ -825,7 +824,7 @@ public class Canvas { */ public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) { - return native_quickReject(mNativeCanvas, left, top, right, bottom); + return native_quickReject(mNativeCanvasWrapper, left, top, right, bottom); } /** @@ -839,7 +838,7 @@ public class Canvas { * @return true if the current clip is non-empty. */ public boolean getClipBounds(Rect bounds) { - return native_getClipBounds(mNativeCanvas, bounds); + return native_getClipBounds(mNativeCanvasWrapper, bounds); } /** @@ -862,7 +861,7 @@ public class Canvas { * @param b blue component (0..255) of the color to draw onto the canvas */ public void drawRGB(int r, int g, int b) { - native_drawRGB(mNativeCanvas, r, g, b); + native_drawRGB(mNativeCanvasWrapper, r, g, b); } /** @@ -875,7 +874,7 @@ public class Canvas { * @param b blue component (0..255) of the color to draw onto the canvas */ public void drawARGB(int a, int r, int g, int b) { - native_drawARGB(mNativeCanvas, a, r, g, b); + native_drawARGB(mNativeCanvasWrapper, a, r, g, b); } /** @@ -885,7 +884,7 @@ public class Canvas { * @param color the color to draw onto the canvas */ public void drawColor(int color) { - native_drawColor(mNativeCanvas, color); + native_drawColor(mNativeCanvasWrapper, color); } /** @@ -896,7 +895,7 @@ public class Canvas { * @param mode the porter-duff mode to apply to the color */ public void drawColor(int color, PorterDuff.Mode mode) { - native_drawColor(mNativeCanvas, color, mode.nativeInt); + native_drawColor(mNativeCanvasWrapper, color, mode.nativeInt); } /** @@ -907,7 +906,7 @@ public class Canvas { * @param paint The paint used to draw onto the canvas */ public void drawPaint(Paint paint) { - native_drawPaint(mNativeCanvas, paint.mNativePaint); + native_drawPaint(mNativeCanvasWrapper, paint.mNativePaint); } /** @@ -953,7 +952,7 @@ public class Canvas { * @param paint The paint used to draw the line */ public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) { - native_drawLine(mNativeCanvas, startX, startY, stopX, stopY, paint.mNativePaint); + native_drawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.mNativePaint); } /** @@ -985,7 +984,7 @@ public class Canvas { * @param paint The paint used to draw the rect */ public void drawRect(RectF rect, Paint paint) { - native_drawRect(mNativeCanvas, rect, paint.mNativePaint); + native_drawRect(mNativeCanvasWrapper, rect, paint.mNativePaint); } /** @@ -1011,7 +1010,7 @@ public class Canvas { * @param paint The paint used to draw the rect */ public void drawRect(float left, float top, float right, float bottom, Paint paint) { - native_drawRect(mNativeCanvas, left, top, right, bottom, paint.mNativePaint); + native_drawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.mNativePaint); } /** @@ -1024,7 +1023,7 @@ public class Canvas { if (oval == null) { throw new NullPointerException(); } - native_drawOval(mNativeCanvas, oval, paint.mNativePaint); + native_drawOval(mNativeCanvasWrapper, oval, paint.mNativePaint); } /** @@ -1038,7 +1037,7 @@ public class Canvas { * @param paint The paint used to draw the circle */ public void drawCircle(float cx, float cy, float radius, Paint paint) { - native_drawCircle(mNativeCanvas, cx, cy, radius, paint.mNativePaint); + native_drawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.mNativePaint); } /** @@ -1069,7 +1068,7 @@ public class Canvas { if (oval == null) { throw new NullPointerException(); } - native_drawArc(mNativeCanvas, oval, startAngle, sweepAngle, + native_drawArc(mNativeCanvasWrapper, oval, startAngle, sweepAngle, useCenter, paint.mNativePaint); } @@ -1096,7 +1095,7 @@ public class Canvas { */ public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint) { - native_drawRoundRect(mNativeCanvas, left, top, right, bottom, rx, ry, paint.mNativePaint); + native_drawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, paint.mNativePaint); } /** @@ -1107,7 +1106,7 @@ public class Canvas { * @param paint The paint used to draw the path */ public void drawPath(Path path, Paint paint) { - native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint); + native_drawPath(mNativeCanvasWrapper, path.ni(), paint.mNativePaint); } /** @@ -1171,7 +1170,7 @@ public class Canvas { */ public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { throwIfCannotDraw(bitmap); - native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top, + native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top, paint != null ? paint.mNativePaint : 0, mDensity, mScreenDensity, bitmap.mDensity); } @@ -1202,7 +1201,7 @@ public class Canvas { throw new NullPointerException(); } throwIfCannotDraw(bitmap); - native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst, + native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst, paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity); } @@ -1233,7 +1232,7 @@ public class Canvas { throw new NullPointerException(); } throwIfCannotDraw(bitmap); - native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst, + native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), src, dst, paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity); } @@ -1285,7 +1284,7 @@ public class Canvas { return; } // punch down to native for the actual draw - native_drawBitmap(mNativeCanvas, colors, offset, stride, x, y, width, height, hasAlpha, + native_drawBitmap(mNativeCanvasWrapper, colors, offset, stride, x, y, width, height, hasAlpha, paint != null ? paint.mNativePaint : 0); } @@ -1313,7 +1312,7 @@ public class Canvas { * @param paint May be null. The paint used to draw the bitmap */ public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { - nativeDrawBitmapMatrix(mNativeCanvas, bitmap.ni(), matrix.ni(), + nativeDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.ni(), matrix.ni(), paint != null ? paint.mNativePaint : 0); } @@ -1367,7 +1366,7 @@ public class Canvas { // no mul by 2, since we need only 1 color per vertex checkRange(colors.length, colorOffset, count); } - nativeDrawBitmapMesh(mNativeCanvas, bitmap.ni(), meshWidth, meshHeight, + nativeDrawBitmapMesh(mNativeCanvasWrapper, bitmap.ni(), meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint != null ? paint.mNativePaint : 0); } @@ -1430,7 +1429,7 @@ public class Canvas { if (indices != null) { checkRange(indices.length, indexOffset, indexCount); } - nativeDrawVertices(mNativeCanvas, mode.nativeInt, vertexCount, verts, + nativeDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts, vertOffset, texs, texOffset, colors, colorOffset, indices, indexOffset, indexCount, paint.mNativePaint); } @@ -1449,7 +1448,7 @@ public class Canvas { (text.length - index - count)) < 0) { throw new IndexOutOfBoundsException(); } - native_drawText(mNativeCanvas, text, index, count, x, y, paint.mBidiFlags, + native_drawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } @@ -1463,7 +1462,7 @@ public class Canvas { * @param paint The paint used for the text (e.g. color, size, style) */ public void drawText(String text, float x, float y, Paint paint) { - native_drawText(mNativeCanvas, text, 0, text.length(), x, y, paint.mBidiFlags, + native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } @@ -1482,7 +1481,7 @@ public class Canvas { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } - native_drawText(mNativeCanvas, text, start, end, x, y, paint.mBidiFlags, + native_drawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } @@ -1502,7 +1501,7 @@ public class Canvas { public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) { if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { - native_drawText(mNativeCanvas, text.toString(), start, end, x, y, + native_drawText(mNativeCanvasWrapper, text.toString(), start, end, x, y, paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawText(this, start, end, x, y, @@ -1510,7 +1509,7 @@ public class Canvas { } else { char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); - native_drawText(mNativeCanvas, buf, 0, end - start, x, y, + native_drawText(mNativeCanvasWrapper, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); TemporaryBuffer.recycle(buf); } @@ -1553,7 +1552,7 @@ public class Canvas { throw new IllegalArgumentException("unknown dir: " + dir); } - native_drawTextRun(mNativeCanvas, text, index, count, + native_drawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount, x, y, dir, paint.mNativePaint, paint.mNativeTypeface); } @@ -1591,7 +1590,7 @@ public class Canvas { if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { - native_drawTextRun(mNativeCanvas, text.toString(), start, end, + native_drawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart, contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawTextRun(this, start, end, @@ -1601,7 +1600,7 @@ public class Canvas { int len = end - start; char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); - native_drawTextRun(mNativeCanvas, buf, start - contextStart, len, + native_drawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len, 0, contextLen, x, y, flags, paint.mNativePaint, paint.mNativeTypeface); TemporaryBuffer.recycle(buf); } @@ -1626,7 +1625,7 @@ public class Canvas { if (index < 0 || index + count > text.length || count*2 > pos.length) { throw new IndexOutOfBoundsException(); } - native_drawPosText(mNativeCanvas, text, index, count, pos, + native_drawPosText(mNativeCanvasWrapper, text, index, count, pos, paint.mNativePaint); } @@ -1646,7 +1645,7 @@ public class Canvas { if (text.length()*2 > pos.length) { throw new ArrayIndexOutOfBoundsException(); } - native_drawPosText(mNativeCanvas, text, pos, paint.mNativePaint); + native_drawPosText(mNativeCanvasWrapper, text, pos, paint.mNativePaint); } /** @@ -1667,7 +1666,7 @@ public class Canvas { if (index < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } - native_drawTextOnPath(mNativeCanvas, text, index, count, + native_drawTextOnPath(mNativeCanvasWrapper, text, index, count, path.ni(), hOffset, vOffset, paint.mBidiFlags, paint.mNativePaint); } @@ -1687,7 +1686,7 @@ public class Canvas { */ public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) { if (text.length() > 0) { - native_drawTextOnPath(mNativeCanvas, text, path.ni(), hOffset, vOffset, + native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset, paint.mBidiFlags, paint.mNativePaint); } } @@ -1761,8 +1760,10 @@ public class Canvas { public static native void freeTextLayoutCaches(); private static native long initRaster(long nativeBitmapOrZero); - private static native void copyNativeCanvasState(long nativeSrcCanvas, - long nativeDstCanvas); + private static native long initCanvas(long canvasHandle); + private static native void native_setBitmap(long canvasHandle, + long bitmapHandle, + boolean copyState); private static native int native_saveLayer(long nativeCanvas, RectF bounds, long nativePaint, diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java index 6ff5f4f..befac92 100644 --- a/graphics/java/android/graphics/NinePatch.java +++ b/graphics/java/android/graphics/NinePatch.java @@ -164,12 +164,12 @@ public class NinePatch { } void drawSoftware(Canvas canvas, RectF location, Paint paint) { - nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk, + nativeDraw(canvas.getNativeCanvasWrapper(), location, mBitmap.ni(), mNativeChunk, paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity); } void drawSoftware(Canvas canvas, Rect location, Paint paint) { - nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk, + nativeDraw(canvas.getNativeCanvasWrapper(), location, mBitmap.ni(), mNativeChunk, paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity); } diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java index a16c099..de458af 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -107,7 +107,7 @@ public class Picture { if (mRecordingCanvas != null) { endRecording(); } - nativeDraw(canvas.getNativeCanvas(), mNativePicture); + nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture); } /** diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 9a63fa3..304502e 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -39,8 +39,8 @@ import org.xmlpull.v1.XmlPullParserFactory; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; +import java.util.Stack; /** * This lets you create a drawable based on an XML vector graphic It can be @@ -58,9 +58,26 @@ import java.util.HashMap; * <dd>Used to defined the size of the virtual canvas the paths are drawn on. * The size is defined using the attributes <code>android:viewportHeight</code> * <code>android:viewportWidth</code></dd> + * <dt><code><group></code></dt> + * <dd>Defines a group of paths or subgroups, plus transformation information. + * The transformations are defined in the same coordinates as the viewport. + * And the transformations are applied in the order of scale, rotate then translate. </dd> + * <dt><code>android:rotation</code> + * <dd>The degrees of rotation of the group.</dd></dt> + * <dt><code>android:pivotX</code> + * <dd>The X coordinate of the pivot for the scale and rotation of the group</dd></dt> + * <dt><code>android:pivotY</code> + * <dd>The Y coordinate of the pivot for the scale and rotation of the group</dd></dt> + * <dt><code>android:scaleX</code> + * <dd>The amount of scale on the X Coordinate</dd></dt> + * <dt><code>android:scaleY</code> + * <dd>The amount of scale on the Y coordinate</dd></dt> + * <dt><code>android:translateX</code> + * <dd>The amount of translation on the X coordinate</dd></dt> + * <dt><code>android:translateY</code> + * <dd>The amount of translation on the Y coordinate</dd></dt> * <dt><code><path></code></dt> - * <dd>Defines paths to be drawn. Multiple paths can be defined in one xml file. - * The paths are drawn in the order of their definition order. + * <dd>Defines paths to be drawn. * <dl> * <dt><code>android:name</code> * <dd>Defines the name of the path.</dd></dt> @@ -76,12 +93,6 @@ import java.util.HashMap; * <dd>The width a path stroke</dd></dt> * <dt><code>android:strokeOpacity</code> * <dd>The opacity of a path stroke</dd></dt> - * <dt><code>android:rotation</code> - * <dd>The amount to rotation the path stroke.</dd></dt> - * <dt><code>android:pivotX</code> - * <dd>The X coordinate of the center of rotation of a path</dd></dt> - * <dt><code>android:pivotY</code> - * <dd>The Y coordinate of the center of rotation of a path</dd></dt> * <dt><code>android:fillOpacity</code> * <dd>The opacity to fill the path with</dd></dt> * <dt><code>android:trimPathStart</code> @@ -101,13 +112,13 @@ import java.util.HashMap; * <dd>Sets the Miter limit for a stroked path</dd></dt> * </dl> * </dd> - * @hide */ public class VectorDrawable extends Drawable { private static final String LOGTAG = VectorDrawable.class.getSimpleName(); private static final String SHAPE_SIZE = "size"; private static final String SHAPE_VIEWPORT = "viewport"; + private static final String SHAPE_GROUP = "group"; private static final String SHAPE_PATH = "path"; private static final String SHAPE_VECTOR = "vector"; @@ -119,6 +130,8 @@ public class VectorDrawable extends Drawable { private static final int LINEJOIN_ROUND = 1; private static final int LINEJOIN_BEVEL = 2; + private static final boolean DBG_VECTOR_DRAWABLE = false; + private final VectorDrawableState mVectorState; private int mAlpha = 0xFF; @@ -266,14 +279,20 @@ public class VectorDrawable extends Drawable { boolean noSizeTag = true; boolean noViewportTag = true; + boolean noGroupTag = true; boolean noPathTag = true; - VGroup currentGroup = new VGroup(); + // Use a stack to help to build the group tree. + // The top of the stack is always the current group. + final Stack<VGroup> groupStack = new Stack<VGroup>(); + groupStack.push(pathRenderer.mRootGroup); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG) { final String tagName = parser.getName(); + final VGroup currentGroup = groupStack.peek(); + if (SHAPE_PATH.equals(tagName)) { final VPath path = new VPath(); path.inflate(res, attrs, theme); @@ -285,12 +304,27 @@ public class VectorDrawable extends Drawable { } else if (SHAPE_VIEWPORT.equals(tagName)) { pathRenderer.parseViewport(res, attrs); noViewportTag = false; + } else if (SHAPE_GROUP.equals(tagName)) { + VGroup newChildGroup = new VGroup(); + newChildGroup.inflate(res, attrs, theme); + currentGroup.mChildGroupList.add(newChildGroup); + groupStack.push(newChildGroup); + noGroupTag = false; + } + } else if (eventType == XmlPullParser.END_TAG) { + final String tagName = parser.getName(); + if (SHAPE_GROUP.equals(tagName)) { + groupStack.pop(); } } - eventType = parser.next(); } + // Print the tree out for debug. + if (DBG_VECTOR_DRAWABLE) { + printGroupTree(pathRenderer.mRootGroup, 0); + } + if (noSizeTag || noViewportTag || noPathTag) { final StringBuffer tag = new StringBuffer(); @@ -315,12 +349,24 @@ public class VectorDrawable extends Drawable { throw new XmlPullParserException("no " + tag + " defined"); } - pathRenderer.mCurrentGroup = currentGroup; - // post parse cleanup - pathRenderer.parseFinish(); return pathRenderer; } + private void printGroupTree(VGroup currentGroup, int level) { + String indent = ""; + for (int i = 0 ; i < level ; i++) { + indent += " "; + } + // Print the current node + Log.v(LOGTAG, indent + "current group is :" + currentGroup.getName() + + " rotation is " + currentGroup.mRotate); + Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString()); + // Then print all the children + for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) { + printGroupTree(currentGroup.mChildGroupList.get(i), level + 1); + } + } + private void setPathRenderer(VPathRenderer pathRenderer) { mVectorState.mVPathRenderer = pathRenderer; } @@ -333,6 +379,7 @@ public class VectorDrawable extends Drawable { public VectorDrawableState(VectorDrawableState copy) { if (copy != null) { mChangingConfigurations = copy.mChangingConfigurations; + // TODO: Make sure the constant state are handled correctly. mVPathRenderer = new VPathRenderer(copy.mVPathRenderer); mPadding = new Rect(copy.mPadding); } @@ -360,35 +407,42 @@ public class VectorDrawable extends Drawable { } private static class VPathRenderer { + /* Right now the internal data structure is organized as a tree. + * Each node can be a group node, or a path. + * A group node can have groups or paths as children, but a path node has + * no children. + * One example can be: + * Root Group + * / | \ + * Group Path Group + * / \ | + * Path Path Path + * + */ + private final VGroup mRootGroup; + private final Path mPath = new Path(); private final Path mRenderPath = new Path(); - private final Matrix mMatrix = new Matrix(); + private static final Matrix IDENTITY_MATRIX = new Matrix(); - private VPath[] mCurrentPaths; private Paint mStrokePaint; private Paint mFillPaint; private ColorFilter mColorFilter; private PathMeasure mPathMeasure; - private VGroup mCurrentGroup = new VGroup(); + private float mBaseWidth = 0; + private float mBaseHeight = 0; + private float mViewportWidth = 0; + private float mViewportHeight = 0; - float mBaseWidth = 0; - float mBaseHeight = 0; - float mViewportWidth = 0; - float mViewportHeight = 0; + private final Matrix mFinalPathMatrix = new Matrix(); public VPathRenderer() { + mRootGroup = new VGroup(); } public VPathRenderer(VPathRenderer copy) { - mCurrentGroup = copy.mCurrentGroup; - if (copy.mCurrentPaths != null) { - mCurrentPaths = new VPath[copy.mCurrentPaths.length]; - for (int i = 0; i < mCurrentPaths.length; i++) { - mCurrentPaths[i] = new VPath(copy.mCurrentPaths[i]); - } - } - + mRootGroup = copy.mRootGroup; mBaseWidth = copy.mBaseWidth; mBaseHeight = copy.mBaseHeight; mViewportWidth = copy.mViewportHeight; @@ -396,24 +450,59 @@ public class VectorDrawable extends Drawable { } public boolean canApplyTheme() { - final ArrayList<VPath> paths = mCurrentGroup.mVGList; + // If one of the paths can apply theme, then return true; + return recursiveCanApplyTheme(mRootGroup); + } + + private boolean recursiveCanApplyTheme(VGroup currentGroup) { + // We can do a tree traverse here, if there is one path return true, + // then we return true for the whole tree. + final ArrayList<VPath> paths = currentGroup.mPathList; for (int j = paths.size() - 1; j >= 0; j--) { final VPath path = paths.get(j); if (path.canApplyTheme()) { return true; } } + + final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList; + + for (int i = 0; i < childGroups.size(); i++) { + VGroup childGroup = childGroups.get(i); + if (childGroup.canApplyTheme() + || recursiveCanApplyTheme(childGroup)) { + return true; + } + } return false; } public void applyTheme(Theme t) { - final ArrayList<VPath> paths = mCurrentGroup.mVGList; + // Apply theme to every path of the tree. + recursiveApplyTheme(mRootGroup, t); + } + + private void recursiveApplyTheme(VGroup currentGroup, Theme t) { + // We can do a tree traverse here, apply theme to all paths which + // can apply theme. + final ArrayList<VPath> paths = currentGroup.mPathList; for (int j = paths.size() - 1; j >= 0; j--) { final VPath path = paths.get(j); if (path.canApplyTheme()) { path.applyTheme(t); } } + + final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList; + + for (int i = 0; i < childGroups.size(); i++) { + VGroup childGroup = childGroups.get(i); + if (childGroup.canApplyTheme()) { + childGroup.applyTheme(t); + } + recursiveApplyTheme(childGroup, t); + } + } public void setColorFilter(ColorFilter colorFilter) { @@ -426,107 +515,109 @@ public class VectorDrawable extends Drawable { if (mStrokePaint != null) { mStrokePaint.setColorFilter(colorFilter); } + } - public void draw(Canvas canvas, int w, int h) { - if (mCurrentPaths == null) { - Log.e(LOGTAG,"mCurrentPaths == null"); - return; + private void drawGroupTree(VGroup currentGroup, Matrix currentMatrix, + Canvas canvas, int w, int h) { + // Calculate current group's matrix by preConcat the parent's and + // and the current one on the top of the stack. + // Basically the Mfinal = Mviewport * M0 * M1 * M2; + // Mi the local matrix at level i of the group tree. + currentGroup.mStackedMatrix.set(currentMatrix); + + currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix); + + drawPath(currentGroup, canvas, w, h); + // Draw the group tree in post order. + for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) { + drawGroupTree(currentGroup.mChildGroupList.get(i), + currentGroup.mStackedMatrix, canvas, w, h); } + } - for (int i = 0; i < mCurrentPaths.length; i++) { - if (mCurrentPaths[i] != null) { - drawPath(mCurrentPaths[i], canvas, w, h); - } - } + public void draw(Canvas canvas, int w, int h) { + // Travese the tree in pre-order to draw. + drawGroupTree(mRootGroup, IDENTITY_MATRIX, canvas, w, h); } - private void drawPath(VPath vPath, Canvas canvas, int w, int h) { + private void drawPath(VGroup vGroup, Canvas canvas, int w, int h) { final float scale = Math.min(h / mViewportHeight, w / mViewportWidth); - vPath.toPath(mPath); - final Path path = mPath; - - if (vPath.mTrimPathStart != 0.0f || vPath.mTrimPathEnd != 1.0f) { - float start = (vPath.mTrimPathStart + vPath.mTrimPathOffset) % 1.0f; - float end = (vPath.mTrimPathEnd + vPath.mTrimPathOffset) % 1.0f; + mFinalPathMatrix.set(vGroup.mStackedMatrix); + mFinalPathMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f); + mFinalPathMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f); - if (mPathMeasure == null) { - mPathMeasure = new PathMeasure(); - } - mPathMeasure.setPath(mPath, false); - - float len = mPathMeasure.getLength(); - start = start * len; - end = end * len; - path.reset(); - if (start > end) { - mPathMeasure.getSegment(start, len, path, true); - mPathMeasure.getSegment(0f, end, path, true); - } else { - mPathMeasure.getSegment(start, end, path, true); - } - path.rLineTo(0, 0); // fix bug in measure - } + ArrayList<VPath> paths = vGroup.getPaths(); + for (int i = 0; i < paths.size(); i++) { + VPath vPath = paths.get(i); + vPath.toPath(mPath); + final Path path = mPath; - mRenderPath.reset(); - mMatrix.reset(); + if (vPath.mTrimPathStart != 0.0f || vPath.mTrimPathEnd != 1.0f) { + float start = (vPath.mTrimPathStart + vPath.mTrimPathOffset) % 1.0f; + float end = (vPath.mTrimPathEnd + vPath.mTrimPathOffset) % 1.0f; - mMatrix.postRotate(vPath.mRotate, vPath.mPivotX, vPath.mPivotY); - mMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f); - mMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f); + if (mPathMeasure == null) { + mPathMeasure = new PathMeasure(); + } + mPathMeasure.setPath(mPath, false); + + float len = mPathMeasure.getLength(); + start = start * len; + end = end * len; + path.reset(); + if (start > end) { + mPathMeasure.getSegment(start, len, path, true); + mPathMeasure.getSegment(0f, end, path, true); + } else { + mPathMeasure.getSegment(start, end, path, true); + } + path.rLineTo(0, 0); // fix bug in measure + } - mRenderPath.addPath(path, mMatrix); + mRenderPath.reset(); - if (vPath.mClip) { - canvas.clipPath(mRenderPath, Region.Op.REPLACE); - } + mRenderPath.addPath(path, mFinalPathMatrix); - if (vPath.mFillColor != 0) { - if (mFillPaint == null) { - mFillPaint = new Paint(); - mFillPaint.setColorFilter(mColorFilter); - mFillPaint.setStyle(Paint.Style.FILL); - mFillPaint.setAntiAlias(true); + if (vPath.mClip) { + canvas.clipPath(mRenderPath, Region.Op.REPLACE); } - mFillPaint.setColor(vPath.mFillColor); - canvas.drawPath(mRenderPath, mFillPaint); - } + if (vPath.mFillColor != 0) { + if (mFillPaint == null) { + mFillPaint = new Paint(); + mFillPaint.setColorFilter(mColorFilter); + mFillPaint.setStyle(Paint.Style.FILL); + mFillPaint.setAntiAlias(true); + } - if (vPath.mStrokeColor != 0) { - if (mStrokePaint == null) { - mStrokePaint = new Paint(); - mStrokePaint.setColorFilter(mColorFilter); - mStrokePaint.setStyle(Paint.Style.STROKE); - mStrokePaint.setAntiAlias(true); + mFillPaint.setColor(vPath.mFillColor); + canvas.drawPath(mRenderPath, mFillPaint); } - final Paint strokePaint = mStrokePaint; - if (vPath.mStrokeLineJoin != null) { - strokePaint.setStrokeJoin(vPath.mStrokeLineJoin); - } + if (vPath.mStrokeColor != 0) { + if (mStrokePaint == null) { + mStrokePaint = new Paint(); + mStrokePaint.setColorFilter(mColorFilter); + mStrokePaint.setStyle(Paint.Style.STROKE); + mStrokePaint.setAntiAlias(true); + } - if (vPath.mStrokeLineCap != null) { - strokePaint.setStrokeCap(vPath.mStrokeLineCap); - } + final Paint strokePaint = mStrokePaint; + if (vPath.mStrokeLineJoin != null) { + strokePaint.setStrokeJoin(vPath.mStrokeLineJoin); + } - strokePaint.setStrokeMiter(vPath.mStrokeMiterlimit * scale); - strokePaint.setColor(vPath.mStrokeColor); - strokePaint.setStrokeWidth(vPath.mStrokeWidth * scale); - canvas.drawPath(mRenderPath, strokePaint); - } - } + if (vPath.mStrokeLineCap != null) { + strokePaint.setStrokeCap(vPath.mStrokeLineCap); + } - /** - * Build the "current" path based on the current group - * TODO: improve memory use & performance or move to C++ - */ - public void parseFinish() { - final Collection<VPath> paths = mCurrentGroup.getPaths(); - mCurrentPaths = paths.toArray(new VPath[paths.size()]); - for (int i = 0; i < mCurrentPaths.length; i++) { - mCurrentPaths[i] = new VPath(mCurrentPaths[i]); + strokePaint.setStrokeMiter(vPath.mStrokeMiterlimit * scale); + strokePaint.setColor(vPath.mStrokeColor); + strokePaint.setStrokeWidth(vPath.mStrokeWidth * scale); + canvas.drawPath(mRenderPath, strokePaint); + } } } @@ -568,20 +659,133 @@ public class VectorDrawable extends Drawable { private static class VGroup { private final HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>(); - private final ArrayList<VPath> mVGList = new ArrayList<VPath>(); + private final ArrayList<VPath> mPathList = new ArrayList<VPath>(); + private final ArrayList<VGroup> mChildGroupList = new ArrayList<VGroup>(); + + private float mRotate = 0; + private float mPivotX = 0; + private float mPivotY = 0; + private float mScaleX = 1; + private float mScaleY = 1; + private float mTranslateX = 0; + private float mTranslateY = 0; + private float mAlpha = 1; + + // mLocalMatrix is parsed from the XML. + private final Matrix mLocalMatrix = new Matrix(); + // mStackedMatrix is only used when drawing, it combines all the + // parents' local matrices with the current one. + private final Matrix mStackedMatrix = new Matrix(); + + private int[] mThemeAttrs; + + private String mName = null; + + public String getName() { + return mName; + } + + public Matrix getLocalMatrix() { + return mLocalMatrix; + } public void add(VPath path) { String id = path.getID(); mVGPathMap.put(id, path); - mVGList.add(path); + mPathList.add(path); } + public boolean canApplyTheme() { + return mThemeAttrs != null; + } + + public void applyTheme(Theme t) { + if (mThemeAttrs == null) { + return; + } + + final TypedArray a = t.resolveAttributes( + mThemeAttrs, R.styleable.VectorDrawablePath); + + mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate); + mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX); + mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY); + mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX); + mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY); + mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX); + mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY); + mAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mAlpha); + updateLocalMatrix(); + if (a.hasValue(R.styleable.VectorDrawableGroup_name)) { + mName = a.getString(R.styleable.VectorDrawableGroup_name); + } + a.recycle(); + } + + public void inflate(Resources res, AttributeSet attrs, Theme theme) { + final TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.VectorDrawableGroup); + final int[] themeAttrs = a.extractThemeAttrs(); + + mThemeAttrs = themeAttrs; + // NOTE: The set of attributes loaded here MUST match the + // set of attributes loaded in applyTheme. + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_rotation] == 0) { + mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate); + } + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotX] == 0) { + mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX); + } + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_pivotY] == 0) { + mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY); + } + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleX] == 0) { + mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX); + } + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_scaleY] == 0) { + mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY); + } + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateX] == 0) { + mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX); + } + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_translateY] == 0) { + mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY); + } + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_name] == 0) { + mName = a.getString(R.styleable.VectorDrawableGroup_name); + } + + if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_alpha] == 0) { + mAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mAlpha); + } + + updateLocalMatrix(); + a.recycle(); + } + + private void updateLocalMatrix() { + // The order we apply is the same as the + // RenderNode.cpp::applyViewPropertyTransforms(). + mLocalMatrix.reset(); + mLocalMatrix.postTranslate(-mPivotX, -mPivotY); + mLocalMatrix.postScale(mScaleX, mScaleY); + mLocalMatrix.postRotate(mRotate, 0, 0); + mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY); + } + /** * Must return in order of adding * @return ordered list of paths */ - public Collection<VPath> getPaths() { - return mVGList; + public ArrayList<VPath> getPaths() { + return mPathList; } } @@ -599,10 +803,6 @@ public class VectorDrawable extends Drawable { int mFillRule; float mFillOpacity = Float.NaN; - float mRotate = 0; - float mPivotX = 0; - float mPivotY = 0; - float mTrimPathStart = 0; float mTrimPathEnd = 1; float mTrimPathOffset = 0; @@ -614,18 +814,11 @@ public class VectorDrawable extends Drawable { private VNode[] mNode = null; private String mId; - private int[] mCheckState = new int[MAX_STATES]; - private boolean[] mCheckValue = new boolean[MAX_STATES]; - private int mNumberOfStates = 0; public VPath() { // Empty constructor. } - public VPath(VPath p) { - copyFrom(p); - } - public void toPath(Path path) { path.reset(); if (mNode != null) { @@ -690,18 +883,6 @@ public class VectorDrawable extends Drawable { mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity); } - if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_rotation] == 0) { - mRotate = a.getFloat(R.styleable.VectorDrawablePath_rotation, mRotate); - } - - if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pivotX] == 0) { - mPivotX = a.getFloat(R.styleable.VectorDrawablePath_pivotX, mPivotX); - } - - if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pivotY] == 0) { - mPivotY = a.getFloat(R.styleable.VectorDrawablePath_pivotY, mPivotY); - } - if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_strokeLineCap] == 0) { mStrokeLineCap = getStrokeLineCap( @@ -780,10 +961,6 @@ public class VectorDrawable extends Drawable { mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor); mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity, mFillOpacity); - mRotate = a.getFloat(R.styleable.VectorDrawablePath_rotation, mRotate); - mPivotX = a.getFloat(R.styleable.VectorDrawablePath_pivotX, mPivotX); - mPivotY = a.getFloat(R.styleable.VectorDrawablePath_pivotY, mPivotY); - mStrokeLineCap = getStrokeLineCap(a.getInt( R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap); mStrokeLineJoin = getStrokeLineJoin(a.getInt( @@ -802,6 +979,7 @@ public class VectorDrawable extends Drawable { R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart); updateColorAlphas(); + a.recycle(); } private void updateColorAlphas() { @@ -904,33 +1082,6 @@ public class VectorDrawable extends Drawable { } return list.toArray(new VectorDrawable.VNode[list.size()]); } - - public void copyFrom(VPath p1) { - mNode = new VNode[p1.mNode.length]; - for (int i = 0; i < mNode.length; i++) { - mNode[i] = new VNode(p1.mNode[i]); - } - mId = p1.mId; - mStrokeColor = p1.mStrokeColor; - mFillColor = p1.mFillColor; - mStrokeWidth = p1.mStrokeWidth; - mRotate = p1.mRotate; - mPivotX = p1.mPivotX; - mPivotY = p1.mPivotY; - mTrimPathStart = p1.mTrimPathStart; - mTrimPathEnd = p1.mTrimPathEnd; - mTrimPathOffset = p1.mTrimPathOffset; - mStrokeLineCap = p1.mStrokeLineCap; - mStrokeLineJoin = p1.mStrokeLineJoin; - mStrokeMiterlimit = p1.mStrokeMiterlimit; - mNumberOfStates = p1.mNumberOfStates; - for (int i = 0; i < mNumberOfStates; i++) { - mCheckState[i] = p1.mCheckState[i]; - mCheckValue[i] = p1.mCheckValue[i]; - } - - mFillRule = p1.mFillRule; - } } private static class VNode { diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 442f327..e5c8898 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -71,8 +71,6 @@ ifeq ($(USE_OPENGL_RENDERER),true) $(LOCAL_PATH)/../../include/utils \ external/skia/src/core - include external/stlport/libstlport.mk - LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE_CLASS := SHARED_LIBRARIES @@ -80,16 +78,15 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_MODULE := libhwui LOCAL_MODULE_TAGS := optional + include external/stlport/libstlport.mk + ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT - LOCAL_SHARED_LIBRARIES += libRS libRScpp libstlport + LOCAL_SHARED_LIBRARIES += libRS libRScpp LOCAL_C_INCLUDES += \ $(intermediates) \ frameworks/rs/cpp \ - frameworks/rs \ - external/stlport/stlport \ - bionic/ \ - bionic/libstdc++/include + frameworks/rs endif ifndef HWUI_COMPILE_SYMBOLS diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 96c6292..f418c9b 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -80,10 +80,6 @@ void DisplayListData::cleanupResources() { delete paths.itemAt(i); } - for (size_t i = 0; i < matrices.size(); i++) { - delete matrices.itemAt(i); - } - bitmapResources.clear(); ownedBitmapResources.clear(); patchResources.clear(); @@ -91,7 +87,6 @@ void DisplayListData::cleanupResources() { paints.clear(); regions.clear(); paths.clear(); - matrices.clear(); layers.clear(); } diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 11e78b0..7b7dc16 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -125,7 +125,6 @@ public: Vector<const SkPath*> paths; SortedVector<const SkPath*> sourcePaths; Vector<const SkRegion*> regions; - Vector<const SkMatrix*> matrices; Vector<Layer*> layers; uint32_t functorCount; bool hasDrawOps; diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index ea3e7a8..9212b9d 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -472,7 +472,7 @@ private: class SetMatrixOp : public StateOp { public: - SetMatrixOp(const SkMatrix* matrix) + SetMatrixOp(const SkMatrix& matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -480,22 +480,22 @@ public: } virtual void output(int level, uint32_t logFlags) const { - if (mMatrix) { - OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix)); - } else { + if (mMatrix.isIdentity()) { OP_LOGS("SetMatrix (reset)"); + } else { + OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); } } virtual const char* name() { return "SetMatrix"; } private: - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class ConcatMatrixOp : public StateOp { public: - ConcatMatrixOp(const SkMatrix* matrix) + ConcatMatrixOp(const SkMatrix& matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -503,13 +503,13 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix)); + OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); } virtual const char* name() { return "ConcatMatrix"; } private: - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class ClipOp : public StateOp { @@ -746,10 +746,10 @@ protected: class DrawBitmapMatrixOp : public DrawBoundedOp { public: - DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) + DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) { mLocalBounds.set(0, 0, bitmap->width(), bitmap->height()); - const mat4 transform(*matrix); + const mat4 transform(matrix); transform.mapRect(mLocalBounds); } @@ -758,7 +758,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(mMatrix)); + OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(&mMatrix)); } virtual const char* name() { return "DrawBitmapMatrix"; } @@ -770,7 +770,7 @@ public: private: const SkBitmap* mBitmap; - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class DrawBitmapRectOp : public DrawBoundedOp { @@ -788,7 +788,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING, + OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING, mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds)); } @@ -978,7 +978,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawPatch"; } @@ -1060,7 +1060,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, @@ -1111,7 +1111,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); + OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); } virtual const char* name() { return "DrawRoundRect"; } @@ -1175,7 +1175,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawOval"; } @@ -1195,7 +1195,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d", + OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d", RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter); } @@ -1232,7 +1232,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawPath"; } diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 229afdf..0e47c6e 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -141,14 +141,12 @@ void DisplayListRenderer::skew(float sx, float sy) { StatefulBaseRenderer::skew(sx, sy); } -void DisplayListRenderer::setMatrix(const SkMatrix* matrix) { - matrix = refMatrix(matrix); +void DisplayListRenderer::setMatrix(const SkMatrix& matrix) { addStateOp(new (alloc()) SetMatrixOp(matrix)); StatefulBaseRenderer::setMatrix(matrix); } -void DisplayListRenderer::concatMatrix(const SkMatrix* matrix) { - matrix = refMatrix(matrix); +void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) { addStateOp(new (alloc()) ConcatMatrixOp(matrix)); StatefulBaseRenderer::concatMatrix(matrix); } @@ -203,10 +201,9 @@ status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float left, flo return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) { bitmap = refBitmap(bitmap); - matrix = refMatrix(matrix); paint = refPaint(paint); addDrawOp(new (alloc()) DrawBitmapMatrixOp(bitmap, matrix, paint)); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index f0ae00f..195b00b 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -86,8 +86,8 @@ public: virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - virtual void setMatrix(const SkMatrix* matrix); - virtual void concatMatrix(const SkMatrix* matrix); + virtual void setMatrix(const SkMatrix& matrix); + virtual void concatMatrix(const SkMatrix& matrix); // Clip virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); @@ -106,7 +106,7 @@ public: // Bitmap-based virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint); - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint); virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, @@ -233,17 +233,6 @@ private: return regionCopy; } - inline const SkMatrix* refMatrix(const SkMatrix* matrix) { - if (matrix) { - // Copying the matrix is cheap and prevents against the user changing - // the original matrix before the operation that uses it - const SkMatrix* copy = new SkMatrix(*matrix); - mDisplayListData->matrices.add(copy); - return copy; - } - return matrix; - } - inline Layer* refLayer(Layer* layer) { mDisplayListData->layers.add(layer); mCaches.resourceCache.incrementRefcount(layer); diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 4407ab0..bf0ab5c 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -592,7 +592,7 @@ void FontRenderer::setFont(const SkPaint* paint, const mat4& matrix) { } FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { + uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) { checkInit(); DropShadow image; @@ -613,8 +613,9 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co Rect bounds; mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); - uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; - uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; + uint32_t intRadius = Blur::convertRadiusToInt(radius); + uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; + uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; uint32_t maxSize = Caches::getInstance().maxTextureSize; if (paddedWidth > maxSize || paddedHeight > maxSize) { @@ -635,8 +636,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co memset(dataBuffer, 0, size); - int penX = radius - bounds.left; - int penY = radius - bounds.bottom; + int penX = intRadius - bounds.left; + int penY = intRadius - bounds.bottom; if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { // text has non-whitespace, so draw and blur to create the shadow @@ -727,9 +728,10 @@ void FontRenderer::removeFont(const Font* font) { } } -void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) { +void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { + uint32_t intRadius = Blur::convertRadiusToInt(radius); #ifdef ANDROID_ENABLE_RENDERSCRIPT - if (width * height * radius >= RS_MIN_INPUT_CUTOFF) { + if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); if (mRs == 0) { @@ -768,12 +770,12 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int } #endif - float *gaussian = new float[2 * radius + 1]; - Blur::generateGaussianWeights(gaussian, radius); + float *gaussian = new float[2 * intRadius + 1]; + Blur::generateGaussianWeights(gaussian, intRadius); uint8_t* scratch = new uint8_t[width * height]; - Blur::horizontal(gaussian, radius, *image, scratch, width, height); - Blur::vertical(gaussian, radius, scratch, *image, width, height); + Blur::horizontal(gaussian, intRadius, *image, scratch, width, height); + Blur::vertical(gaussian, intRadius, scratch, *image, width, height); delete[] gaussian; delete[] scratch; diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 9259028..8ce22b0 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -129,7 +129,7 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, uint32_t radius, const float* positions); + uint32_t len, int numGlyphs, float radius, const float* positions); void setTextureFiltering(bool linearFiltering) { mLinearFiltering = linearFiltering; @@ -218,7 +218,7 @@ private: int32_t width, int32_t height); // the input image handle may have its pointer replaced (to avoid copies) - void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius); + void blurImage(uint8_t** image, int32_t width, int32_t height, float radius); }; }; // namespace uirenderer diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 71836dd..cd09f86 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2042,10 +2042,10 @@ status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float left, float to return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) { Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); - const mat4 transform(*matrix); + const mat4 transform(matrix); transform.mapRect(r); if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index fc27947..0f953a5 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -170,7 +170,7 @@ public: const SkPaint* paint); status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint); - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint); virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 5a49f38..d9c06d3 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -346,7 +346,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { float left, top, offset; uint32_t width, height; - PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height); + PathCache::computePathBounds(t->path, &t->paint, left, top, offset, width, height); PathTexture* texture = t->texture; texture->left = left; @@ -357,7 +357,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { if (width <= mMaxTextureSize && height <= mMaxTextureSize) { SkBitmap* bitmap = new SkBitmap(); - drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); + drawPath(t->path, &t->paint, *bitmap, left, top, offset, width, height); t->setResult(bitmap); } else { texture->width = 0; diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 847853a..bcfb367 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -293,7 +293,7 @@ private: class PathTask: public Task<SkBitmap*> { public: PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture): - path(path), paint(paint), texture(texture) { + path(path), paint(*paint), texture(texture) { } ~PathTask() { @@ -301,7 +301,8 @@ private: } const SkPath* path; - const SkPaint* paint; + //copied, since input paint may not be immutable + const SkPaint paint; PathTexture* texture; }; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d964efc..baf372a 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -223,9 +223,9 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { renderer.translate(properties().getLeft(), properties().getTop()); } if (properties().getStaticMatrix()) { - renderer.concatMatrix(properties().getStaticMatrix()); + renderer.concatMatrix(*properties().getStaticMatrix()); } else if (properties().getAnimationMatrix()) { - renderer.concatMatrix(properties().getAnimationMatrix()); + renderer.concatMatrix(*properties().getAnimationMatrix()); } if (properties().hasTransformMatrix()) { if (properties().isTransformTranslateOnly()) { diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h index 23cab0e..320895c 100644 --- a/libs/hwui/Renderer.h +++ b/libs/hwui/Renderer.h @@ -170,8 +170,8 @@ public: virtual void scale(float sx, float sy) = 0; virtual void skew(float sx, float sy) = 0; - virtual void setMatrix(const SkMatrix* matrix) = 0; - virtual void concatMatrix(const SkMatrix* matrix) = 0; + virtual void setMatrix(const SkMatrix& matrix) = 0; + virtual void concatMatrix(const SkMatrix& matrix) = 0; // clip virtual const Rect& getLocalClipBounds() const = 0; @@ -193,7 +193,7 @@ public: // Bitmap-based virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint) = 0; - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0; virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp index 90039e9..fae25a6 100644 --- a/libs/hwui/StatefulBaseRenderer.cpp +++ b/libs/hwui/StatefulBaseRenderer.cpp @@ -121,20 +121,16 @@ void StatefulBaseRenderer::skew(float sx, float sy) { mSnapshot->transform->skew(sx, sy); } -void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) { - if (matrix) { - mSnapshot->transform->load(*matrix); - } else { - mSnapshot->transform->loadIdentity(); - } +void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) { + mSnapshot->transform->load(matrix); } void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) { mSnapshot->transform->load(matrix); } -void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) { - mat4 transform(*matrix); +void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) { + mat4 transform(matrix); mSnapshot->transform->multiply(transform); } diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h index 057006b..f38c752 100644 --- a/libs/hwui/StatefulBaseRenderer.h +++ b/libs/hwui/StatefulBaseRenderer.h @@ -75,9 +75,9 @@ public: virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - virtual void setMatrix(const SkMatrix* matrix); + virtual void setMatrix(const SkMatrix& matrix); void setMatrix(const Matrix4& matrix); // internal only convenience method - virtual void concatMatrix(const SkMatrix* matrix); + virtual void concatMatrix(const SkMatrix& matrix); void concatMatrix(const Matrix4& matrix); // internal only convenience method // Clip diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp index c020b40..877a422 100644 --- a/libs/hwui/utils/Blur.cpp +++ b/libs/hwui/utils/Blur.cpp @@ -19,6 +19,7 @@ #include <math.h> #include "Blur.h" +#include "MathUtils.h" namespace android { namespace uirenderer { @@ -35,6 +36,17 @@ float Blur::convertSigmaToRadius(float sigma) { return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f; } +// if the original radius was on an integer boundary and the resulting radius +// is within the conversion error tolerance then we attempt to snap to the +// original integer boundary. +uint32_t Blur::convertRadiusToInt(float radius) { + const float radiusCeil = ceilf(radius); + if (MathUtils::areEqual(radiusCeil, radius)) { + return radiusCeil; + } + return radius; +} + /** * HWUI has used a slightly different equation than Skia to generate the value * for sigma and to preserve compatibility we have kept that logic. diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h index 79aff65..b145333 100644 --- a/libs/hwui/utils/Blur.h +++ b/libs/hwui/utils/Blur.h @@ -27,8 +27,12 @@ class Blur { public: // If radius > 0, return the corresponding sigma, else return 0 ANDROID_API static float convertRadiusToSigma(float radius); - // If sigma > 0.6, return the corresponding radius, else return 0 + // If sigma > 0.5, return the corresponding radius, else return 0 ANDROID_API static float convertSigmaToRadius(float sigma); + // If the original radius was on an integer boundary then after the sigma to + // radius conversion a small rounding error may be introduced. This function + // accounts for that error and snaps to the appropriate integer boundary. + static uint32_t convertRadiusToInt(float radius); static void generateGaussianWeights(float* weights, int32_t radius); static void horizontal(float* weights, int32_t radius, const uint8_t* source, diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 5a3aaab..bb23a36 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -26,7 +26,6 @@ import java.util.HashSet; import java.util.Set; /** - * @hide * A class to encapsulate a collection of attributes describing information about an audio * player or recorder. */ diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index 57274ee..b07d2c5 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -156,4 +156,10 @@ public class AudioFormat { } } + /** @removed */ + public AudioFormat() + { + throw new UnsupportedOperationException("There is no valid usage of this constructor"); + } + } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 8ca303b..84d4ab6 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2837,18 +2837,22 @@ public class AudioManager { } /** - * Indicate A2DP sink connection state change. + * Indicate A2DP source or sink connection state change. * @param device Bluetooth device connected/disconnected * @param state new connection state (BluetoothProfile.STATE_xxx) + * @param profile profile for the A2DP device + * (either {@link android.bluetooth.BluetoothProfile.A2DP} or + * {@link android.bluetooth.BluetoothProfile.A2DP_SINK}) * @return a delay in ms that the caller should wait before broadcasting * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent. * {@hide} */ - public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state) { + public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, + int profile) { IAudioService service = getService(); int delay = 0; try { - delay = service.setBluetoothA2dpDeviceConnectionState(device, state); + delay = service.setBluetoothA2dpDeviceConnectionState(device, state, profile); } catch (RemoteException e) { Log.e(TAG, "Dead object in setBluetoothA2dpDeviceConnectionState "+e); } finally { diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index fd346d5..74f39b7 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -177,7 +177,8 @@ public class AudioService extends IAudioService.Stub { // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 100; - private static final int MSG_SET_A2DP_CONNECTION_STATE = 101; + private static final int MSG_SET_A2DP_SRC_CONNECTION_STATE = 101; + private static final int MSG_SET_A2DP_SINK_CONNECTION_STATE = 102; // end of messages handled under wakelock private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; @@ -2384,10 +2385,10 @@ public class AudioService extends IAudioService.Stub { synchronized (mConnectedDevices) { int state = mA2dp.getConnectionState(btDevice); int delay = checkSendBecomingNoisyIntent( - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0); + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0); queueMsgUnderWakeLock(mAudioHandler, - MSG_SET_A2DP_CONNECTION_STATE, + MSG_SET_A2DP_SINK_CONNECTION_STATE, state, 0, btDevice, @@ -2397,6 +2398,22 @@ public class AudioService extends IAudioService.Stub { } break; + case BluetoothProfile.A2DP_SINK: + deviceList = proxy.getConnectedDevices(); + if (deviceList.size() > 0) { + btDevice = deviceList.get(0); + synchronized (mConnectedDevices) { + int state = proxy.getConnectionState(btDevice); + queueMsgUnderWakeLock(mAudioHandler, + MSG_SET_A2DP_SRC_CONNECTION_STATE, + state, + 0, + btDevice, + 0 /* delay */); + } + } + break; + case BluetoothProfile.HEADSET: synchronized (mScoClients) { // Discard timeout message @@ -2465,6 +2482,15 @@ public class AudioService extends IAudioService.Stub { } break; + case BluetoothProfile.A2DP_SINK: + synchronized (mConnectedDevices) { + if (mConnectedDevices.containsKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) { + makeA2dpSrcUnavailable( + mConnectedDevices.get(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)); + } + } + break; + case BluetoothProfile.HEADSET: synchronized (mScoClients) { mBluetoothHeadset = null; @@ -2880,14 +2906,22 @@ public class AudioService extends IAudioService.Stub { } } - public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state) + public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile) { int delay; + if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) { + throw new IllegalArgumentException("invalid profile " + profile); + } synchronized (mConnectedDevices) { - delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0); + if (profile == BluetoothProfile.A2DP) { + delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0); + } else { + delay = 0; + } queueMsgUnderWakeLock(mAudioHandler, - MSG_SET_A2DP_CONNECTION_STATE, + (profile == BluetoothProfile.A2DP ? + MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE), state, 0, device, @@ -2933,54 +2967,56 @@ public class AudioService extends IAudioService.Stub { return name + "_" + suffix; } - public synchronized void readSettings() { - // force maximum volume on all streams if fixed volume property is set - if (mUseFixedVolume) { - mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax); - return; - } - // do not read system stream volume from settings: this stream is always aliased - // to another stream type and its volume is never persisted. Values in settings can - // only be stale values - if ((mStreamType == AudioSystem.STREAM_SYSTEM) || - (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) { - int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; - synchronized (mCameraSoundForced) { - if (mCameraSoundForced) { - index = mIndexMax; + public void readSettings() { + synchronized (VolumeStreamState.class) { + // force maximum volume on all streams if fixed volume property is set + if (mUseFixedVolume) { + mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax); + return; + } + // do not read system stream volume from settings: this stream is always aliased + // to another stream type and its volume is never persisted. Values in settings can + // only be stale values + if ((mStreamType == AudioSystem.STREAM_SYSTEM) || + (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) { + int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; + synchronized (mCameraSoundForced) { + if (mCameraSoundForced) { + index = mIndexMax; + } } + mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); + return; } - mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); - return; - } - int remainingDevices = AudioSystem.DEVICE_OUT_ALL; + int remainingDevices = AudioSystem.DEVICE_OUT_ALL; - for (int i = 0; remainingDevices != 0; i++) { - int device = (1 << i); - if ((device & remainingDevices) == 0) { - continue; - } - remainingDevices &= ~device; - - // retrieve current volume for device - String name = getSettingNameForDevice(device); - // if no volume stored for current stream and device, use default volume if default - // device, continue otherwise - int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ? - AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1; - int index = Settings.System.getIntForUser( - mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT); - if (index == -1) { - continue; - } + for (int i = 0; remainingDevices != 0; i++) { + int device = (1 << i); + if ((device & remainingDevices) == 0) { + continue; + } + remainingDevices &= ~device; + + // retrieve current volume for device + String name = getSettingNameForDevice(device); + // if no volume stored for current stream and device, use default volume if default + // device, continue otherwise + int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ? + AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1; + int index = Settings.System.getIntForUser( + mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT); + if (index == -1) { + continue; + } - // ignore settings for fixed volume devices: volume should always be at max or 0 - if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) && - ((device & mFixedVolumeDevices) != 0)) { - mIndex.put(device, (index != 0) ? mIndexMax : 0); - } else { - mIndex.put(device, getValidIndex(10 * index)); + // ignore settings for fixed volume devices: volume should always be at max or 0 + if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) && + ((device & mFixedVolumeDevices) != 0)) { + mIndex.put(device, (index != 0) ? mIndexMax : 0); + } else { + mIndex.put(device, getValidIndex(10 * index)); + } } } } @@ -2998,32 +3034,34 @@ public class AudioService extends IAudioService.Stub { AudioSystem.setStreamVolumeIndex(mStreamType, index, device); } - public synchronized void applyAllVolumes() { - // apply default volume first: by convention this will reset all - // devices volumes in audio policy manager to the supplied value - int index; - if (isMuted()) { - index = 0; - } else { - index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10; - } - AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT); - // then apply device specific volumes - Set set = mIndex.entrySet(); - Iterator i = set.iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); - int device = ((Integer)entry.getKey()).intValue(); - if (device != AudioSystem.DEVICE_OUT_DEFAULT) { - if (isMuted()) { - index = 0; - } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && - mAvrcpAbsVolSupported) { - index = (mIndexMax + 5)/10; - } else { - index = ((Integer)entry.getValue() + 5)/10; + public void applyAllVolumes() { + synchronized (VolumeStreamState.class) { + // apply default volume first: by convention this will reset all + // devices volumes in audio policy manager to the supplied value + int index; + if (isMuted()) { + index = 0; + } else { + index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10; + } + AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT); + // then apply device specific volumes + Set set = mIndex.entrySet(); + Iterator i = set.iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + int device = ((Integer)entry.getKey()).intValue(); + if (device != AudioSystem.DEVICE_OUT_DEFAULT) { + if (isMuted()) { + index = 0; + } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && + mAvrcpAbsVolSupported) { + index = (mIndexMax + 5)/10; + } else { + index = ((Integer)entry.getValue() + 5)/10; + } + AudioSystem.setStreamVolumeIndex(mStreamType, index, device); } - AudioSystem.setStreamVolumeIndex(mStreamType, index, device); } } } @@ -3033,94 +3071,104 @@ public class AudioService extends IAudioService.Stub { device); } - public synchronized boolean setIndex(int index, int device) { - int oldIndex = getIndex(device); - index = getValidIndex(index); - synchronized (mCameraSoundForced) { - if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) { - index = mIndexMax; + public boolean setIndex(int index, int device) { + synchronized (VolumeStreamState.class) { + int oldIndex = getIndex(device); + index = getValidIndex(index); + synchronized (mCameraSoundForced) { + if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) { + index = mIndexMax; + } } - } - mIndex.put(device, index); - - if (oldIndex != index) { - // Apply change to all streams using this one as alias - // if changing volume of current device, also change volume of current - // device on aliased stream - boolean currentDevice = (device == getDeviceForStream(mStreamType)); - int numStreamTypes = AudioSystem.getNumStreamTypes(); - for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { - if (streamType != mStreamType && - mStreamVolumeAlias[streamType] == mStreamType) { - int scaledIndex = rescaleIndex(index, mStreamType, streamType); - mStreamStates[streamType].setIndex(scaledIndex, - device); - if (currentDevice) { + mIndex.put(device, index); + + if (oldIndex != index) { + // Apply change to all streams using this one as alias + // if changing volume of current device, also change volume of current + // device on aliased stream + boolean currentDevice = (device == getDeviceForStream(mStreamType)); + int numStreamTypes = AudioSystem.getNumStreamTypes(); + for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { + if (streamType != mStreamType && + mStreamVolumeAlias[streamType] == mStreamType) { + int scaledIndex = rescaleIndex(index, mStreamType, streamType); mStreamStates[streamType].setIndex(scaledIndex, - getDeviceForStream(streamType)); + device); + if (currentDevice) { + mStreamStates[streamType].setIndex(scaledIndex, + getDeviceForStream(streamType)); + } } } + return true; + } else { + return false; } - return true; - } else { - return false; } } - public synchronized int getIndex(int device) { - Integer index = mIndex.get(device); - if (index == null) { - // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT - index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT); + public int getIndex(int device) { + synchronized (VolumeStreamState.class) { + Integer index = mIndex.get(device); + if (index == null) { + // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT + index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT); + } + return index.intValue(); } - return index.intValue(); } public int getMaxIndex() { return mIndexMax; } - public synchronized void setAllIndexes(VolumeStreamState srcStream) { - int srcStreamType = srcStream.getStreamType(); - // apply default device volume from source stream to all devices first in case - // some devices are present in this stream state but not in source stream state - int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); - index = rescaleIndex(index, srcStreamType, mStreamType); - Set set = mIndex.entrySet(); - Iterator i = set.iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); - entry.setValue(index); - } - // Now apply actual volume for devices in source stream state - set = srcStream.mIndex.entrySet(); - i = set.iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); - int device = ((Integer)entry.getKey()).intValue(); - index = ((Integer)entry.getValue()).intValue(); + public void setAllIndexes(VolumeStreamState srcStream) { + synchronized (VolumeStreamState.class) { + int srcStreamType = srcStream.getStreamType(); + // apply default device volume from source stream to all devices first in case + // some devices are present in this stream state but not in source stream state + int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); index = rescaleIndex(index, srcStreamType, mStreamType); - - setIndex(index, device); + Set set = mIndex.entrySet(); + Iterator i = set.iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + entry.setValue(index); + } + // Now apply actual volume for devices in source stream state + set = srcStream.mIndex.entrySet(); + i = set.iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + int device = ((Integer)entry.getKey()).intValue(); + index = ((Integer)entry.getValue()).intValue(); + index = rescaleIndex(index, srcStreamType, mStreamType); + + setIndex(index, device); + } } } - public synchronized void setAllIndexesToMax() { - Set set = mIndex.entrySet(); - Iterator i = set.iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); - entry.setValue(mIndexMax); + public void setAllIndexesToMax() { + synchronized (VolumeStreamState.class) { + Set set = mIndex.entrySet(); + Iterator i = set.iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + entry.setValue(mIndexMax); + } } } - public synchronized void mute(IBinder cb, boolean state) { - VolumeDeathHandler handler = getDeathHandler(cb, state); - if (handler == null) { - Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); - return; + public void mute(IBinder cb, boolean state) { + synchronized (VolumeStreamState.class) { + VolumeDeathHandler handler = getDeathHandler(cb, state); + if (handler == null) { + Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); + return; + } + handler.mute(state); } - handler.mute(state); } public int getStreamType() { @@ -3729,8 +3777,13 @@ public class AudioService extends IAudioService.Stub { mAudioEventWakeLock.release(); break; - case MSG_SET_A2DP_CONNECTION_STATE: - onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1); + case MSG_SET_A2DP_SRC_CONNECTION_STATE: + onSetA2dpSourceConnectionState((BluetoothDevice)msg.obj, msg.arg1); + mAudioEventWakeLock.release(); + break; + + case MSG_SET_A2DP_SINK_CONNECTION_STATE: + onSetA2dpSinkConnectionState((BluetoothDevice)msg.obj, msg.arg1); mAudioEventWakeLock.release(); break; @@ -3857,6 +3910,23 @@ public class AudioService extends IAudioService.Stub { } // must be called synchronized on mConnectedDevices + private void makeA2dpSrcAvailable(String address) { + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, + AudioSystem.DEVICE_STATE_AVAILABLE, + address); + mConnectedDevices.put( new Integer(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP), + address); + } + + // must be called synchronized on mConnectedDevices + private void makeA2dpSrcUnavailable(String address) { + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, + AudioSystem.DEVICE_STATE_UNAVAILABLE, + address); + mConnectedDevices.remove(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP); + } + + // must be called synchronized on mConnectedDevices private void cancelA2dpDeviceTimeout() { mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT); } @@ -3866,9 +3936,11 @@ public class AudioService extends IAudioService.Stub { return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); } - private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state) + private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state) { - if (DEBUG_VOL) Log.d(TAG, "onSetA2dpConnectionState btDevice="+btDevice+" state="+state); + if (DEBUG_VOL) { + Log.d(TAG, "onSetA2dpSinkConnectionState btDevice="+btDevice+"state="+state); + } if (btDevice == null) { return; } @@ -3927,6 +3999,32 @@ public class AudioService extends IAudioService.Stub { } } + private void onSetA2dpSourceConnectionState(BluetoothDevice btDevice, int state) + { + if (DEBUG_VOL) { + Log.d(TAG, "onSetA2dpSourceConnectionState btDevice="+btDevice+" state="+state); + } + if (btDevice == null) { + return; + } + String address = btDevice.getAddress(); + if (!BluetoothAdapter.checkBluetoothAddress(address)) { + address = ""; + } + + synchronized (mConnectedDevices) { + boolean isConnected = + (mConnectedDevices.containsKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) && + mConnectedDevices.get(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP).equals(address)); + + if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { + makeA2dpSrcUnavailable(address); + } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { + makeA2dpSrcAvailable(address); + } + } + } + public void avrcpSupportsAbsoluteVolume(String address, boolean support) { // address is not used for now, but may be used when multiple a2dp devices are supported synchronized (mA2dpAvrcpLock) { @@ -3992,7 +4090,8 @@ public class AudioService extends IAudioService.Stub { } } - if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) || + if (mAudioHandler.hasMessages(MSG_SET_A2DP_SRC_CONNECTION_STATE) || + mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE) || mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) { delay = 1000; } @@ -4059,8 +4158,9 @@ public class AudioService extends IAudioService.Stub { (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) { setBluetoothA2dpOnInt(true); } - boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0) || - ((device & AudioSystem.DEVICE_IN_ALL_USB) != 0); + boolean isUsb = ((device & ~AudioSystem.DEVICE_OUT_ALL_USB) == 0) || + (((device & AudioSystem.DEVICE_BIT_IN) != 0) && + ((device & ~AudioSystem.DEVICE_IN_ALL_USB) == 0)); handleDeviceConnection((state == 1), device, (isUsb ? name : "")); if (state != 0) { if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) || @@ -4077,7 +4177,7 @@ public class AudioService extends IAudioService.Stub { MUSIC_ACTIVE_POLL_PERIOD_MS); } } - if (!isUsb) { + if (!isUsb && (device != AudioSystem.DEVICE_IN_WIRED_HEADSET)) { sendDeviceConnectionIntent(device, state, name); } } @@ -4093,7 +4193,8 @@ public class AudioService extends IAudioService.Stub { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - int device; + int outDevice; + int inDevice; int state; if (action.equals(Intent.ACTION_DOCK_EVENT)) { @@ -4128,7 +4229,8 @@ public class AudioService extends IAudioService.Stub { } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); - device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; + outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; + inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; String address = null; BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); @@ -4142,10 +4244,10 @@ public class AudioService extends IAudioService.Stub { switch (btClass.getDeviceClass()) { case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: - device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; break; case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; break; } } @@ -4155,7 +4257,9 @@ public class AudioService extends IAudioService.Stub { } boolean connected = (state == BluetoothProfile.STATE_CONNECTED); - if (handleDeviceConnection(connected, device, address)) { + boolean success = handleDeviceConnection(connected, outDevice, address) && + handleDeviceConnection(connected, inDevice, address); + if (success) { synchronized (mScoClients) { if (connected) { mBluetoothHeadsetDevice = btDevice; @@ -4175,8 +4279,8 @@ public class AudioService extends IAudioService.Stub { : "card=" + alsaCard + ";device=" + alsaDevice); // Playback Device - device = AudioSystem.DEVICE_OUT_USB_ACCESSORY; - setWiredDeviceConnectionState(device, state, params); + outDevice = AudioSystem.DEVICE_OUT_USB_ACCESSORY; + setWiredDeviceConnectionState(outDevice, state, params); } else if (action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) { state = intent.getIntExtra("state", 0); @@ -4191,14 +4295,14 @@ public class AudioService extends IAudioService.Stub { // Playback Device if (hasPlayback) { - device = AudioSystem.DEVICE_OUT_USB_DEVICE; - setWiredDeviceConnectionState(device, state, params); + outDevice = AudioSystem.DEVICE_OUT_USB_DEVICE; + setWiredDeviceConnectionState(outDevice, state, params); } // Capture Device if (hasCapture) { - device = AudioSystem.DEVICE_IN_USB_DEVICE; - setWiredDeviceConnectionState(device, state, params); + inDevice = AudioSystem.DEVICE_IN_USB_DEVICE; + setWiredDeviceConnectionState(inDevice, state, params); } } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { boolean broadcast = false; diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index af7a3e1..c8d64ce 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -300,7 +300,7 @@ public class AudioSystem public static final int DEVICE_IN_TV_TUNER = DEVICE_BIT_IN | 0x4000; public static final int DEVICE_IN_LINE = DEVICE_BIT_IN | 0x8000; public static final int DEVICE_IN_SPDIF = DEVICE_BIT_IN | 0x10000; - + public static final int DEVICE_IN_BLUETOOTH_A2DP = DEVICE_BIT_IN | 0x20000; public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT; public static final int DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | @@ -320,6 +320,7 @@ public class AudioSystem DEVICE_IN_TV_TUNER | DEVICE_IN_LINE | DEVICE_IN_SPDIF | + DEVICE_IN_BLUETOOTH_A2DP | DEVICE_IN_DEFAULT); public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET; public static final int DEVICE_IN_ALL_USB = (DEVICE_IN_USB_ACCESSORY | diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 30de4f9..e59623b 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -232,7 +232,7 @@ interface IAudioService { int getMasterStreamType(); void setWiredDeviceConnectionState(int device, int state, String name); - int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state); + int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile); AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index d781336..19b54a6 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -433,16 +433,14 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, case MTP_TYPE_STR: { jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0); + const char* str = (stringValue ? env->GetStringUTFChars(stringValue, NULL) : NULL); if (stringValue) { - const char* str = env->GetStringUTFChars(stringValue, NULL); - if (str == NULL) { - return MTP_RESPONSE_GENERAL_ERROR; - } packet.putString(str); env->ReleaseStringUTFChars(stringValue, str); } else { packet.putEmptyString(); } + env->DeleteLocalRef(stringValue); break; } default: @@ -515,7 +513,7 @@ MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle, break; } default: - ALOGE("unsupported type in getObjectPropertyValue\n"); + ALOGE("unsupported type in setObjectPropertyValue\n"); return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; } diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp index acfcd83..3f37ed1 100644 --- a/native/android/sensor.cpp +++ b/native/android/sensor.cpp @@ -171,8 +171,3 @@ const char* ASensor_getStringType(ASensor const* sensor) { return static_cast<Sensor const*>(sensor)->getStringType().string(); } - -const char* ASensor_getRequiredPermission(ASensor const* sensor) -{ - return static_cast<Sensor const*>(sensor)->getRequiredPermission().string(); -} diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 36c1d5c..ec87c6e 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -88,7 +88,7 @@ public class DefaultContainerService extends IntentService { private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { /** * Creates a new container and copies resource there. - * @param paackageURI the uri of resource to be copied. Can be either + * @param packageURI the uri of resource to be copied. Can be either * a content uri or a file uri * @param cid the id of the secure container that should * be used for creating a secure container into which the resource @@ -101,13 +101,13 @@ public class DefaultContainerService extends IntentService { */ public String copyResourceToContainer(final Uri packageURI, final String cid, final String key, final String resFileName, final String publicResFileName, - boolean isExternal, boolean isForwardLocked) { + boolean isExternal, boolean isForwardLocked, String abiOverride) { if (packageURI == null || cid == null) { return null; } return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName, - isExternal, isForwardLocked); + isExternal, isForwardLocked, abiOverride); } /** @@ -153,13 +153,12 @@ public class DefaultContainerService extends IntentService { /** * Determine the recommended install location for package * specified by file uri location. - * @param fileUri the uri of resource to be copied. Should be a - * file uri + * * @return Returns PackageInfoLite object containing * the package info and recommended app location. */ public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags, - long threshold) { + long threshold, String abiOverride) { PackageInfoLite ret = new PackageInfoLite(); if (packagePath == null) { @@ -191,7 +190,7 @@ public class DefaultContainerService extends IntentService { ret.verifiers = pkg.verifiers; ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, - packagePath, flags, threshold); + packagePath, flags, threshold, abiOverride); return ret; } @@ -208,11 +207,11 @@ public class DefaultContainerService extends IntentService { } @Override - public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked) - throws RemoteException { + public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked, + String abiOverride) throws RemoteException { final File apkFile = new File(packageUri.getPath()); try { - return isUnderExternalThreshold(apkFile, isForwardLocked); + return isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride); } catch (IOException e) { return true; } @@ -265,11 +264,11 @@ public class DefaultContainerService extends IntentService { } @Override - public long calculateInstalledSize(String packagePath, boolean isForwardLocked) - throws RemoteException { + public long calculateInstalledSize(String packagePath, boolean isForwardLocked, + String abiOverride) throws RemoteException { final File packageFile = new File(packagePath); try { - return calculateContainerSize(packageFile, isForwardLocked) * 1024 * 1024; + return calculateContainerSize(packageFile, isForwardLocked, abiOverride) * 1024 * 1024; } catch (IOException e) { /* * Okay, something failed, so let's just estimate it to be 2x @@ -328,7 +327,8 @@ public class DefaultContainerService extends IntentService { } private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName, - String publicResFileName, boolean isExternal, boolean isForwardLocked) { + String publicResFileName, boolean isExternal, boolean isForwardLocked, + String abiOverride) { if (isExternal) { // Make sure the sdcard is mounted. @@ -343,7 +343,22 @@ public class DefaultContainerService extends IntentService { String codePath = packageURI.getPath(); File codeFile = new File(codePath); NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath); - final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + String[] abiList = Build.SUPPORTED_ABIS; + if (abiOverride != null) { + abiList = new String[] { abiOverride }; + } else { + try { + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } + } catch (IOException ioe) { + Slog.w(TAG, "Problem determining ABI for: " + codeFile.getPath()); + return null; + } + } + + final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); // Calculate size of container needed to hold base APK. final int sizeMb; @@ -414,7 +429,7 @@ public class DefaultContainerService extends IntentService { int ret = PackageManager.INSTALL_SUCCEEDED; if (abi >= 0) { ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - sharedLibraryDir, Build.SUPPORTED_ABIS[abi]); + sharedLibraryDir, abiList[abi]); } else if (abi != PackageManager.NO_NATIVE_LIBRARIES) { ret = abi; } @@ -672,7 +687,7 @@ public class DefaultContainerService extends IntentService { private static final int PREFER_EXTERNAL = 2; private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags, - long threshold) { + long threshold, String abiOverride) { int prefer; boolean checkBoth = false; @@ -741,7 +756,7 @@ public class DefaultContainerService extends IntentService { boolean fitsOnSd = false; if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) { try { - fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked); + fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride); } catch (IOException e) { return PackageHelper.RECOMMEND_FAILED_INVALID_URI; } @@ -812,13 +827,13 @@ public class DefaultContainerService extends IntentService { * @return true if file fits * @throws IOException when file does not exist */ - private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked) + private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked, String abiOverride) throws IOException { if (Environment.isExternalStorageEmulated()) { return false; } - final int sizeMb = calculateContainerSize(apkFile, isForwardLocked); + final int sizeMb = calculateContainerSize(apkFile, isForwardLocked, abiOverride); final int availSdMb; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { @@ -832,9 +847,11 @@ public class DefaultContainerService extends IntentService { return availSdMb > sizeMb; } - private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException { + private int calculateContainerSize(File apkFile, boolean forwardLocked, + String abiOverride) throws IOException { NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile); - final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + final int abi = NativeLibraryHelper.findSupportedAbi(handle, + (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS); try { return calculateContainerSize(handle, apkFile, abi, forwardLocked); diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml index f0f7e10..24d66ce 100644 --- a/packages/DocumentsUI/res/values-el/strings.xml +++ b/packages/DocumentsUI/res/values-el/strings.xml @@ -27,7 +27,7 @@ <string name="menu_settings" msgid="6008033148948428823">"Ρυθμίσεις"</string> <string name="menu_open" msgid="432922957274920903">"Άνοιγμα"</string> <string name="menu_save" msgid="2394743337684426338">"Αποθήκευση"</string> - <string name="menu_share" msgid="3075149983979628146">"Κοινοποίηση"</string> + <string name="menu_share" msgid="3075149983979628146">"Κοινή χÏήση"</string> <string name="menu_delete" msgid="8138799623850614177">"ΔιαγÏαφή"</string> <string name="menu_select" msgid="8711270657353563424">"Επιλογή \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string> <string name="mode_selected_count" msgid="459111894725594625">"ΕπιλÎχθηκαν <xliff:g id="COUNT">%1$d</xliff:g>"</string> @@ -51,5 +51,5 @@ <string name="empty" msgid="7858882803708117596">"Δεν υπάÏχουν στοιχεία"</string> <string name="toast_no_application" msgid="1339885974067891667">"Δεν είναι δυνατό το άνοιγμα του αÏχείου"</string> <string name="toast_failed_delete" msgid="2180678019407244069">"Δεν είναι δυνατή η διαγÏαφή οÏισμÎνων εγγÏάφων"</string> - <string name="share_via" msgid="8966594246261344259">"Κοινοποίηση μÎσω"</string> + <string name="share_via" msgid="8966594246261344259">"Κοινή χÏήση μÎσω"</string> </resources> diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml index 8c9030d..e8944ec 100644 --- a/packages/DocumentsUI/res/values-km-rKH/strings.xml +++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml @@ -20,14 +20,14 @@ <string name="title_open" msgid="4353228937663917801">"បើក​ពី"</string> <string name="title_save" msgid="2433679664882857999">"រក្សា​ទុក​ទៅ"</string> <string name="menu_create_dir" msgid="5947289605844398389">"បង្កើážâ€‹ážáž"</string> - <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា​"</string> + <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា"</string> <string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាព​បញ្ជី"</string> <string name="menu_sort" msgid="7677740407158414452">"ážáž˜áŸ’រៀប​ážáž¶áž˜"</string> <string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string> <string name="menu_settings" msgid="6008033148948428823">"ការ​កំណážáŸ‹"</string> <string name="menu_open" msgid="432922957274920903">"បើក"</string> <string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string> - <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក​"</string> + <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string> <string name="menu_delete" msgid="8138799623850614177">"លុប"</string> <string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string> <string name="mode_selected_count" msgid="459111894725594625">"បាន​ជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string> @@ -48,7 +48,7 @@ <string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញ​ឧបករណáŸâ€‹áž€áž˜áŸ’ážšáž·ážâ€‹ážáŸ’ពស់"</string> <string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញ​ទំហំ​ឯកសារ"</string> <string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញ​ទំហំ​ឧបករណáŸ"</string> - <string name="empty" msgid="7858882803708117596">"គ្មានធាážáž»â€‹"</string> + <string name="empty" msgid="7858882803708117596">"គ្មានធាážáž»"</string> <string name="toast_no_application" msgid="1339885974067891667">"មិន​អាច​បើក​ឯកសារ"</string> <string name="toast_failed_delete" msgid="2180678019407244069">"មិន​អាច​លុប​ឯកសារ​មួយ​ចំនួន"</string> <string name="share_via" msgid="8966594246261344259">"ចែករំលែក​ážáž¶áž˜"</string> diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml index 45d1e2a..d67a9fd 100644 --- a/packages/InputDevices/res/values-af/strings.xml +++ b/packages/InputDevices/res/values-af/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreeus"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litaus"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaans (Latyn)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letties"</string> </resources> diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml index 3f62df6..3e84794 100644 --- a/packages/InputDevices/res/values-am/strings.xml +++ b/packages/InputDevices/res/values-am/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ዕብራስጥ"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ሊቱዌኒያኛ"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ስá“ኒሽ (ላቲን)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ላትቪያኛ"</string> </resources> diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml index e70cad4..a922a46 100644 --- a/packages/InputDevices/res/values-ar/strings.xml +++ b/packages/InputDevices/res/values-ar/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"العبرية"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"الليتوانية"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"الإسبانية (اللاتينية)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"اللاتÙية"</string> </resources> diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml index 7582a69..d68a347 100644 --- a/packages/InputDevices/res/values-bg/strings.xml +++ b/packages/InputDevices/res/values-bg/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ИвритÑка клавиатурна подредба"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ЛитовÑка клавиатурна подредба"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ИÑп. клав. подредба (Лат. Ðмерика)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"латвийÑки"</string> </resources> diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml index e38b9a8..6baa5b8 100644 --- a/packages/InputDevices/res/values-ca/strings.xml +++ b/packages/InputDevices/res/values-ca/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreu"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituà "</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanyol (llatÃ)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letó"</string> </resources> diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml index 532f3c0..1c502fe 100644 --- a/packages/InputDevices/res/values-cs/strings.xml +++ b/packages/InputDevices/res/values-cs/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejÅ¡tina"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litevÅ¡tina"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Å¡panÄ›lÅ¡tina (Latinská Amerika)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"LotyÅ¡ská klávesnice"</string> </resources> diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml index 3c907f8..043a5b3 100644 --- a/packages/InputDevices/res/values-da/strings.xml +++ b/packages/InputDevices/res/values-da/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebræisk"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisk"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spansk (latinamerika)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettisk"</string> </resources> diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml index 83be031..04c19e3 100644 --- a/packages/InputDevices/res/values-de/strings.xml +++ b/packages/InputDevices/res/values-de/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebräisch"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisch"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanisch (Lateinisch)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettisch"</string> </resources> diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml index 99f77ed..025a288 100644 --- a/packages/InputDevices/res/values-el/strings.xml +++ b/packages/InputDevices/res/values-el/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ΕβÏαϊκά"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Λιθουανικά"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ισπανικά (Λατινικής ΑμεÏικής)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Λετονικά"</string> </resources> diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml index e22c675..d5797a0 100644 --- a/packages/InputDevices/res/values-en-rGB/strings.xml +++ b/packages/InputDevices/res/values-en-rGB/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string> </resources> diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml index e22c675..d5797a0 100644 --- a/packages/InputDevices/res/values-en-rIN/strings.xml +++ b/packages/InputDevices/res/values-en-rIN/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string> </resources> diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml index 5190fc5..0a9d2f3 100644 --- a/packages/InputDevices/res/values-es-rUS/strings.xml +++ b/packages/InputDevices/res/values-es-rUS/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreo"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (latino)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string> </resources> diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml index a81ed29..6e41abf 100644 --- a/packages/InputDevices/res/values-es/strings.xml +++ b/packages/InputDevices/res/values-es/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreo"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (Latinoamérica)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string> </resources> diff --git a/packages/InputDevices/res/values-et-rEE/strings.xml b/packages/InputDevices/res/values-et-rEE/strings.xml index b90f825..0d931ce 100644 --- a/packages/InputDevices/res/values-et-rEE/strings.xml +++ b/packages/InputDevices/res/values-et-rEE/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Heebrea"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Leedu"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Hispaania (Ladina-Ameerika)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"läti keel"</string> </resources> diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml index 490b5c2..e87fbad 100644 --- a/packages/InputDevices/res/values-fa/strings.xml +++ b/packages/InputDevices/res/values-fa/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"عبری"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"لیتوانیایی"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"اسپانیایی (لاتین)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"لتونیایی"</string> </resources> diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml index 060d0e7..5b39dfd 100644 --- a/packages/InputDevices/res/values-fi/strings.xml +++ b/packages/InputDevices/res/values-fi/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"heprea"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"liettua"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"espanja (Latinalainen Amerikka)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvialainen"</string> </resources> diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml index 8fc9f79..9973918 100644 --- a/packages/InputDevices/res/values-fr-rCA/strings.xml +++ b/packages/InputDevices/res/values-fr-rCA/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hébreu"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituanien"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espagnol (latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letton"</string> </resources> diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml index b029b02..fa2977b 100644 --- a/packages/InputDevices/res/values-fr/strings.xml +++ b/packages/InputDevices/res/values-fr/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hébreu"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituanien"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espagnol (latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letton"</string> </resources> diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml index 8398c92..77cb8fe 100644 --- a/packages/InputDevices/res/values-hi/strings.xml +++ b/packages/InputDevices/res/values-hi/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"हिबà¥à¤°à¥‚"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"लिथà¥à¤†à¤¨à¤¿à¤¯à¤¾à¤ˆ"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"सà¥à¤ªà¥‡à¤¨à¤¿à¤¶ (लैटिन)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"लातवियाई"</string> </resources> diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml index 68c868f..bad973d 100644 --- a/packages/InputDevices/res/values-hr/strings.xml +++ b/packages/InputDevices/res/values-hr/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejski"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litavski"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Å¡panjolski (Latinska Amerika)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijska"</string> </resources> diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml index af6a571..510591d 100644 --- a/packages/InputDevices/res/values-hu/strings.xml +++ b/packages/InputDevices/res/values-hu/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"héber"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litván"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"spanyol (latin-amerikai)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"lett"</string> </resources> diff --git a/packages/InputDevices/res/values-hy-rAM/strings.xml b/packages/InputDevices/res/values-hy-rAM/strings.xml index 068e559..9ffa8bb 100644 --- a/packages/InputDevices/res/values-hy-rAM/strings.xml +++ b/packages/InputDevices/res/values-hy-rAM/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ÔµÕ¢Ö€Õ¡ÕµÕ¥Ö€Õ¥Õ¶"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Ô¼Õ«Õ¿Õ¾Õ¥Ö€Õ¥Õ¶"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ô»Õ½ÕºÕ¡Õ¶Õ¥Ö€Õ¥Õ¶ (Ô¼Õ¡Õ¿Õ«Õ¶Õ¡Õ¯Õ¡Õ¶)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Õ¬Õ¡Õ¿Õ«Õ·Õ¥Ö€Õ¥Õ¶"</string> </resources> diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml index d32ac98..fccfa67 100644 --- a/packages/InputDevices/res/values-in/strings.xml +++ b/packages/InputDevices/res/values-in/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ibrani"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuania"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanyol (Latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvi"</string> </resources> diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml index 280a23c..83dba70 100644 --- a/packages/InputDevices/res/values-it/strings.xml +++ b/packages/InputDevices/res/values-it/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ebraico"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spagnolo (America Latina)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettone"</string> </resources> diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml index 9bb66d8..26fe662 100644 --- a/packages/InputDevices/res/values-iw/strings.xml +++ b/packages/InputDevices/res/values-iw/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"עברית"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ליט×ית"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ספרדית (×œ×˜×™× ×™×ª)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"לטבית"</string> </resources> diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml index 26b1094..e2b154d 100644 --- a/packages/InputDevices/res/values-ja/strings.xml +++ b/packages/InputDevices/res/values-ja/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ヘブライ語"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"リトアニア語"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"スペイン語(ä¸å—米)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ラトビア語"</string> </resources> diff --git a/packages/InputDevices/res/values-ka-rGE/strings.xml b/packages/InputDevices/res/values-ka-rGE/strings.xml index 35085a1..eff4b04 100644 --- a/packages/InputDevices/res/values-ka-rGE/strings.xml +++ b/packages/InputDevices/res/values-ka-rGE/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ებრáƒáƒ£áƒšáƒ˜"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ლიტვური"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ესპáƒáƒœáƒ£áƒ ი (ლáƒáƒ—ინური)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ლáƒáƒ¢áƒ•იური"</string> </resources> diff --git a/packages/InputDevices/res/values-km-rKH/strings.xml b/packages/InputDevices/res/values-km-rKH/strings.xml index ea3b755..60a28b1 100644 --- a/packages/InputDevices/res/values-km-rKH/strings.xml +++ b/packages/InputDevices/res/values-km-rKH/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"អ៊ីស្រាអែល"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"លីទុយអានី"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"អáŸážŸáŸ’ប៉ាញ (ឡាážáž¶áŸ†áž„​)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ឡាážážœáž¸áž™áŸ‰áž¶"</string> </resources> diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml index e360ed0..3f563d1 100644 --- a/packages/InputDevices/res/values-ko/strings.xml +++ b/packages/InputDevices/res/values-ko/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"히브리어"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"리투아니아어"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"스페ì¸ì–´(ë¼í‹´)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ë¼íŠ¸ë¹„ì•„ì–´"</string> </resources> diff --git a/packages/InputDevices/res/values-lo-rLA/strings.xml b/packages/InputDevices/res/values-lo-rLA/strings.xml index fc37501..fb3fe17 100644 --- a/packages/InputDevices/res/values-lo-rLA/strings.xml +++ b/packages/InputDevices/res/values-lo-rLA/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ຮີບຣິວ"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"​ລິ​ທົວ​ນຽນ"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"​ສະ​à»àº›àº™â€‹àº™àº´àº” (ລາ​ຕິນ)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"​ລັດ​ວຽນ"</string> </resources> diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml index c197389..d0eb1f6 100644 --- a/packages/InputDevices/res/values-lt/strings.xml +++ b/packages/InputDevices/res/values-lt/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrajų"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lietuvių"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ispanų (Lotynų Amerika)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvių k."</string> </resources> diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml index 53d2467..0608bf0 100644 --- a/packages/InputDevices/res/values-lv/strings.xml +++ b/packages/InputDevices/res/values-lv/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ivrits"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"LietuvieÅ¡u"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"SpÄņu (latīņu)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"LatvieÅ¡u"</string> </resources> diff --git a/packages/InputDevices/res/values-mn-rMN/strings.xml b/packages/InputDevices/res/values-mn-rMN/strings.xml index 194c577..a28fd2a 100644 --- a/packages/InputDevices/res/values-mn-rMN/strings.xml +++ b/packages/InputDevices/res/values-mn-rMN/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Еврей"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литви"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ИÑпани (Латин)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латви"</string> </resources> diff --git a/packages/InputDevices/res/values-ms-rMY/strings.xml b/packages/InputDevices/res/values-ms-rMY/strings.xml index a3098c5..a1a6d00 100644 --- a/packages/InputDevices/res/values-ms-rMY/strings.xml +++ b/packages/InputDevices/res/values-ms-rMY/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Bahasa Ibrani"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Bahasa Lithuania"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Bahasa Sepanyol (Latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Bahasa Latvia"</string> </resources> diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml index 47cff5c..ad4b704 100644 --- a/packages/InputDevices/res/values-nb/strings.xml +++ b/packages/InputDevices/res/values-nb/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraisk"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauisk"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spansk (latinsk)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvisk"</string> </resources> diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml index e858c9c..c57251e 100644 --- a/packages/InputDevices/res/values-nl/strings.xml +++ b/packages/InputDevices/res/values-nl/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreeuws"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litouws"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaans (Latijns-Amerika)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lets"</string> </resources> diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml index 0ca91ca..39fb3ec 100644 --- a/packages/InputDevices/res/values-pl/strings.xml +++ b/packages/InputDevices/res/values-pl/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrajski"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litewski"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"hiszpaÅ„ski (Ameryka ÅaciÅ„ska)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Å‚otewski"</string> </resources> diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml index d5afe05..3ac3b84 100644 --- a/packages/InputDevices/res/values-pt-rPT/strings.xml +++ b/packages/InputDevices/res/values-pt-rPT/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraico"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string> </resources> diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml index 84cf865..e9a0a38 100644 --- a/packages/InputDevices/res/values-pt/strings.xml +++ b/packages/InputDevices/res/values-pt/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebraico"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituano"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string> </resources> diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml index 5278643..c2392b1 100644 --- a/packages/InputDevices/res/values-ro/strings.xml +++ b/packages/InputDevices/res/values-ro/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Ebraică"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituaniană"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaniolă (America Latină)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letonă"</string> </resources> diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml index 3ae2e53..70ecf6e 100644 --- a/packages/InputDevices/res/values-ru/strings.xml +++ b/packages/InputDevices/res/values-ru/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Иврит"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ЛитовÑкий"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ИÑпанÑкий (ЛатинÑÐºÐ°Ñ Ðмерика)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"латышÑкий"</string> </resources> diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml index 1e51167..d2ee0cf 100644 --- a/packages/InputDevices/res/values-sk/strings.xml +++ b/packages/InputDevices/res/values-sk/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"HebrejÄina"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"LitovÄina"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Å panielÄina (Latinská Amerika)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"LotyÅ¡tina"</string> </resources> diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml index 66e340e..38542ef 100644 --- a/packages/InputDevices/res/values-sl/strings.xml +++ b/packages/InputDevices/res/values-sl/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejÅ¡Äina"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litovÅ¡Äina"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Å¡panÅ¡Äina (Latinska Amerika)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijÅ¡Äina"</string> </resources> diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml index 14ccbf3..dd500e6 100644 --- a/packages/InputDevices/res/values-sr/strings.xml +++ b/packages/InputDevices/res/values-sr/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"хебрејÑки"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"литванÑки"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"шпанÑки (ЛатинÑка Ðмерика)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"летонÑки"</string> </resources> diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml index f3338c6..c2406c0 100644 --- a/packages/InputDevices/res/values-sv/strings.xml +++ b/packages/InputDevices/res/values-sv/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebreiska"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litauiska"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanska (latinamerikansk)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"lettiska"</string> </resources> diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml index 336cc33..f71a696 100644 --- a/packages/InputDevices/res/values-sw/strings.xml +++ b/packages/InputDevices/res/values-sw/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Kiyahudi"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Kilithuania"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Kihispania (Kilatini)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Kilatvia"</string> </resources> diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml index 86a633a..296994b 100644 --- a/packages/InputDevices/res/values-th/strings.xml +++ b/packages/InputDevices/res/values-th/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"ฮิบรู"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ลิทัวเนีย"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"สเปน (ละติน)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ลัตเวีย"</string> </resources> diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml index 702a0cb..d7920ed 100644 --- a/packages/InputDevices/res/values-tl/strings.xml +++ b/packages/InputDevices/res/values-tl/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrew"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lithuanian"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string> </resources> diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml index b3ce0a3..c0c70be 100644 --- a/packages/InputDevices/res/values-tr/strings.xml +++ b/packages/InputDevices/res/values-tr/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"İbranice"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Litvanca"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"İspanyolca (Latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letonca"</string> </resources> diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml index 5193a90..d8152d4 100644 --- a/packages/InputDevices/res/values-uk/strings.xml +++ b/packages/InputDevices/res/values-uk/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Іврит"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ЛитовÑька"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ІÑпанÑька (латиницÑ)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"ЛатвійÑька"</string> </resources> diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml index 2240100..a90c1cd 100644 --- a/packages/InputDevices/res/values-vi/strings.xml +++ b/packages/InputDevices/res/values-vi/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Tiếng Do Thái"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Tiếng Lithuania"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Tiếng Tây Ban Nha (La tinh)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Tiếng Latvia"</string> </resources> diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml index 161b600..206f97c 100644 --- a/packages/InputDevices/res/values-zh-rCN/strings.xml +++ b/packages/InputDevices/res/values-zh-rCN/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯æ¥è¯"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"ç«‹é™¶å®›è¯"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西ç牙è¯ï¼ˆæ‹‰ä¸ç¾Žæ´²ï¼‰"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脱维亚è¯"</string> </resources> diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml index f0df88c..ff5570e 100644 --- a/packages/InputDevices/res/values-zh-rHK/strings.xml +++ b/packages/InputDevices/res/values-zh-rHK/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯來文"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"立陶宛文"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西ç牙文 (拉ä¸ç¾Žæ´²)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脫ç¶äºžæ–‡"</string> </resources> diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml index ca43326..859983d 100644 --- a/packages/InputDevices/res/values-zh-rTW/strings.xml +++ b/packages/InputDevices/res/values-zh-rTW/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"希伯來文"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"立陶宛文"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西ç牙文 (拉ä¸ç¾Žæ´²)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脫ç¶äºžæ–‡"</string> </resources> diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml index 677ed29..9f30f7a 100644 --- a/packages/InputDevices/res/values-zu/strings.xml +++ b/packages/InputDevices/res/values-zu/strings.xml @@ -39,6 +39,5 @@ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Isi-Hebrew"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Isi-Lithuanian"</string> <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Isi-Spanish (Latin)"</string> - <!-- no translation found for keyboard_layout_latvian (4405417142306250595) --> - <skip /> + <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Isi-Latvian"</string> </resources> diff --git a/packages/Keyguard/Android.mk b/packages/Keyguard/Android.mk index 1be44f9..96ed2e7 100644 --- a/packages/Keyguard/Android.mk +++ b/packages/Keyguard/Android.mk @@ -16,8 +16,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files) \ - $(call all-proto-files-under,src) +LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files) LOCAL_MODULE := Keyguard @@ -27,9 +26,6 @@ LOCAL_PRIVILEGED_MODULE := true LOCAL_PROGUARD_FLAG_FILES := proguard.flags -LOCAL_PROTOC_OPTIMIZE_TYPE := nano -LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors - LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml index 18b59f1..a2e54a7 100644 --- a/packages/Keyguard/res/values-km-rKH/strings.xml +++ b/packages/Keyguard/res/values-km-rKH/strings.xml @@ -83,7 +83,7 @@ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string> <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string> <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string> - <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string> + <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string> <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string> <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string> <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string> @@ -120,7 +120,7 @@ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពáŸáž€"</string> <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string> <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string> - <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាážáŸ‹â€‹"</string> + <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាážáŸ‹"</string> <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាážáŸ‹â€‹áž˜áž·áž“​ážáŸ’រឹមážáŸ’រូវ។"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លáŸáž…​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាážáŸ‹â€‹ážšáž”ស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string> diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java b/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java deleted file mode 100644 index 20af2f1..0000000 --- a/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.analytics; - -import com.google.protobuf.nano.CodedOutputByteBufferNano; -import com.google.protobuf.nano.MessageNano; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.AsyncTask; -import android.util.Log; -import android.view.MotionEvent; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * Tracks sessions, touch and sensor events in Keyguard. - * - * A session starts when the user is presented with the Keyguard and ends when the Keyguard is no - * longer visible to the user. - */ -public class KeyguardAnalytics implements SensorEventListener { - - private static final boolean DEBUG = false; - private static final String TAG = "KeyguardAnalytics"; - private static final long TIMEOUT_MILLIS = 11000; // 11 seconds. - - private static final int[] SENSORS = new int[] { - Sensor.TYPE_ACCELEROMETER, - Sensor.TYPE_GYROSCOPE, - Sensor.TYPE_PROXIMITY, - Sensor.TYPE_LIGHT, - Sensor.TYPE_ROTATION_VECTOR, - }; - - private Session mCurrentSession = null; - // Err on the side of caution, so logging is not started after a crash even tough the screen - // is off. - private boolean mScreenOn = false; - private boolean mHidden = false; - - private final SensorManager mSensorManager; - private final SessionTypeAdapter mSessionTypeAdapter; - private final File mAnalyticsFile; - - public KeyguardAnalytics(Context context, SessionTypeAdapter sessionTypeAdapter, - File analyticsFile) { - mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - mSessionTypeAdapter = sessionTypeAdapter; - mAnalyticsFile = analyticsFile; - } - - public Callback getCallback() { - return mCallback; - } - - public interface Callback { - public void onShow(); - public void onHide(); - public void onScreenOn(); - public void onScreenOff(); - public boolean onTouchEvent(MotionEvent ev, int width, int height); - public void onSetOccluded(boolean hidden); - } - - public interface SessionTypeAdapter { - public int getSessionType(); - } - - private void sessionEntrypoint() { - if (mCurrentSession == null && mScreenOn && !mHidden) { - onSessionStart(); - } - } - - private void sessionExitpoint(int result) { - if (mCurrentSession != null) { - onSessionEnd(result); - } - } - - private void onSessionStart() { - int type = mSessionTypeAdapter.getSessionType(); - mCurrentSession = new Session(System.currentTimeMillis(), System.nanoTime(), type); - if (type == Session.TYPE_KEYGUARD_SECURE) { - mCurrentSession.setRedactTouchEvents(); - } - for (int sensorType : SENSORS) { - Sensor s = mSensorManager.getDefaultSensor(sensorType); - if (s != null) { - mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME); - } - } - if (DEBUG) { - Log.d(TAG, "onSessionStart()"); - } - } - - private void onSessionEnd(int result) { - if (DEBUG) { - Log.d(TAG, String.format("onSessionEnd(success=%d)", result)); - } - mSensorManager.unregisterListener(this); - - Session session = mCurrentSession; - mCurrentSession = null; - - session.end(System.currentTimeMillis(), result); - queueSession(session); - } - - private void queueSession(final Session currentSession) { - if (DEBUG) { - Log.i(TAG, "Saving session."); - } - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - try { - byte[] b = writeDelimitedProto(currentSession.toProto()); - OutputStream os = new FileOutputStream(mAnalyticsFile, true /* append */); - if (DEBUG) { - Log.d(TAG, String.format("Serialized size: %d kB.", b.length / 1024)); - } - try { - os.write(b); - os.flush(); - } finally { - try { - os.close(); - } catch (IOException e) { - Log.e(TAG, "Exception while closing file", e); - } - } - } catch (IOException e) { - Log.e(TAG, "Exception while writing file", e); - } - return null; - } - - private byte[] writeDelimitedProto(MessageNano proto) - throws IOException { - byte[] result = new byte[CodedOutputByteBufferNano.computeMessageSizeNoTag(proto)]; - CodedOutputByteBufferNano ob = CodedOutputByteBufferNano.newInstance(result); - ob.writeMessageNoTag(proto); - ob.checkNoSpaceLeft(); - return result; - } - }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - } - - @Override - public synchronized void onSensorChanged(SensorEvent event) { - if (false) { - Log.v(TAG, String.format( - "onSensorChanged(name=%s, values[0]=%f)", - event.sensor.getName(), event.values[0])); - } - if (mCurrentSession != null) { - mCurrentSession.addSensorEvent(event, System.nanoTime()); - enforceTimeout(); - } - } - - private void enforceTimeout() { - if (System.currentTimeMillis() - mCurrentSession.getStartTimestampMillis() - > TIMEOUT_MILLIS) { - onSessionEnd(Session.RESULT_UNKNOWN); - if (DEBUG) { - Log.i(TAG, "Analytics timed out."); - } - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - - private final Callback mCallback = new Callback() { - @Override - public void onShow() { - if (DEBUG) { - Log.d(TAG, "onShow()"); - } - synchronized (KeyguardAnalytics.this) { - sessionEntrypoint(); - } - } - - @Override - public void onHide() { - if (DEBUG) { - Log.d(TAG, "onHide()"); - } - synchronized (KeyguardAnalytics.this) { - sessionExitpoint(Session.RESULT_SUCCESS); - } - } - - @Override - public void onScreenOn() { - if (DEBUG) { - Log.d(TAG, "onScreenOn()"); - } - synchronized (KeyguardAnalytics.this) { - mScreenOn = true; - sessionEntrypoint(); - } - } - - @Override - public void onScreenOff() { - if (DEBUG) { - Log.d(TAG, "onScreenOff()"); - } - synchronized (KeyguardAnalytics.this) { - mScreenOn = false; - sessionExitpoint(Session.RESULT_FAILURE); - } - } - - @Override - public boolean onTouchEvent(MotionEvent ev, int width, int height) { - if (DEBUG) { - Log.v(TAG, "onTouchEvent(ev.action=" - + MotionEvent.actionToString(ev.getAction()) + ")"); - } - synchronized (KeyguardAnalytics.this) { - if (mCurrentSession != null) { - mCurrentSession.addMotionEvent(ev); - mCurrentSession.setTouchArea(width, height); - enforceTimeout(); - } - } - return true; - } - - @Override - public void onSetOccluded(boolean hidden) { - synchronized (KeyguardAnalytics.this) { - if (hidden != mHidden) { - if (DEBUG) { - Log.d(TAG, "onSetOccluded(" + hidden + ")"); - } - mHidden = hidden; - if (hidden) { - // Could have gone to camera on purpose / by falsing or an app could have - // launched on top of the lockscreen. - sessionExitpoint(Session.RESULT_UNKNOWN); - } else { - sessionEntrypoint(); - } - } - } - } - }; - -} diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java b/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java deleted file mode 100644 index e68f751..0000000 --- a/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.analytics; - -import android.graphics.RectF; -import android.util.FloatMath; -import android.util.SparseArray; -import android.view.MotionEvent; - -import java.util.HashMap; -import java.util.Map; - -import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.TouchEvent.BoundingBox; - -/** - * Takes motion events and tracks the length and bounding box of each pointer gesture as well as - * the bounding box of the whole gesture. - */ -public class PointerTracker { - private SparseArray<Pointer> mPointerInfoMap = new SparseArray<Pointer>(); - private RectF mTotalBoundingBox = new RectF(); - - public void addMotionEvent(MotionEvent ev) { - if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { - float x = ev.getX(); - float y = ev.getY(); - mTotalBoundingBox.set(x, y, x, y); - } - for (int i = 0; i < ev.getPointerCount(); i++) { - int id = ev.getPointerId(i); - Pointer pointer = getPointer(id); - float x = ev.getX(i); - float y = ev.getY(i); - boolean down = ev.getActionMasked() == MotionEvent.ACTION_DOWN - || (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN - && ev.getActionIndex() == i); - pointer.addPoint(x, y, down); - mTotalBoundingBox.union(x, y); - } - } - - public float getPointerLength(int id) { - return getPointer(id).length; - } - - public BoundingBox getBoundingBox() { - return boundingBoxFromRect(mTotalBoundingBox); - } - - public BoundingBox getPointerBoundingBox(int id) { - return boundingBoxFromRect(getPointer(id).boundingBox); - } - - private BoundingBox boundingBoxFromRect(RectF f) { - BoundingBox bb = new BoundingBox(); - bb.setHeight(f.height()); - bb.setWidth(f.width()); - return bb; - } - - private Pointer getPointer(int id) { - Pointer p = mPointerInfoMap.get(id); - if (p == null) { - p = new Pointer(); - mPointerInfoMap.put(id, p); - } - return p; - } - - private static class Pointer { - public float length; - public final RectF boundingBox = new RectF(); - - private float mLastX; - private float mLastY; - - public void addPoint(float x, float y, boolean down) { - float deltaX; - float deltaY; - if (down) { - boundingBox.set(x, y, x, y); - length = 0f; - deltaX = 0; - deltaY = 0; - } else { - deltaX = x - mLastX; - deltaY = y - mLastY; - } - mLastX = x; - mLastY = y; - length += FloatMath.sqrt(deltaX * deltaX + deltaY * deltaY); - boundingBox.union(x, y); - } - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/Session.java b/packages/Keyguard/src/com/android/keyguard/analytics/Session.java deleted file mode 100644 index 05f9165..0000000 --- a/packages/Keyguard/src/com/android/keyguard/analytics/Session.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.analytics; - -import android.os.Build; -import android.util.Slog; -import android.view.MotionEvent; - -import java.util.ArrayList; - -import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.SensorEvent; -import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.TouchEvent; - -/** - * Records data about one keyguard session. - * - * The recorded data contains start and end of the session, whether it unlocked the device - * successfully, sensor data and touch data. - * - * If the keyguard is secure, the recorded touch data will correlate or contain the user pattern or - * PIN. If this is not desired, the touch coordinates can be redacted before serialization. - */ -public class Session { - - private static final String TAG = "KeyguardAnalytics"; - private static final boolean DEBUG = false; - - /** - * The user has failed to unlock the device in this session. - */ - public static final int RESULT_FAILURE = KeyguardAnalyticsProtos.Session.FAILURE; - /** - * The user has succeeded in unlocking the device in this session. - */ - public static final int RESULT_SUCCESS = KeyguardAnalyticsProtos.Session.SUCCESS; - - /** - * It is unknown how the session with the keyguard ended. - */ - public static final int RESULT_UNKNOWN = KeyguardAnalyticsProtos.Session.UNKNOWN; - - /** - * This session took place on an insecure keyguard. - */ - public static final int TYPE_KEYGUARD_INSECURE - = KeyguardAnalyticsProtos.Session.KEYGUARD_INSECURE; - - /** - * This session took place on an secure keyguard. - */ - public static final int TYPE_KEYGUARD_SECURE - = KeyguardAnalyticsProtos.Session.KEYGUARD_SECURE; - - /** - * This session took place during a fake wake up of the device. - */ - public static final int TYPE_RANDOM_WAKEUP = KeyguardAnalyticsProtos.Session.RANDOM_WAKEUP; - - - private final PointerTracker mPointerTracker = new PointerTracker(); - - private final long mStartTimestampMillis; - private final long mStartSystemTimeNanos; - private final int mType; - - private boolean mRedactTouchEvents; - private ArrayList<TouchEvent> mMotionEvents = new ArrayList<TouchEvent>(200); - private ArrayList<SensorEvent> mSensorEvents = new ArrayList<SensorEvent>(600); - private int mTouchAreaHeight; - private int mTouchAreaWidth; - - private long mEndTimestampMillis; - private int mResult; - private boolean mEnded; - - public Session(long startTimestampMillis, long startSystemTimeNanos, int type) { - mStartTimestampMillis = startTimestampMillis; - mStartSystemTimeNanos = startSystemTimeNanos; - mType = type; - } - - public void end(long endTimestampMillis, int result) { - mEnded = true; - mEndTimestampMillis = endTimestampMillis; - mResult = result; - } - - public void addMotionEvent(MotionEvent motionEvent) { - if (mEnded) { - return; - } - mPointerTracker.addMotionEvent(motionEvent); - mMotionEvents.add(protoFromMotionEvent(motionEvent)); - } - - public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) { - if (mEnded) { - return; - } - SensorEvent event = protoFromSensorEvent(eventOrig, systemTimeNanos); - mSensorEvents.add(event); - if (DEBUG) { - Slog.v(TAG, String.format("addSensorEvent(name=%s, values[0]=%f", - event.getType(), event.values[0])); - } - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("Session{"); - sb.append("mType=").append(mType); - sb.append(", mStartTimestampMillis=").append(mStartTimestampMillis); - sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos); - sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis); - sb.append(", mResult=").append(mResult); - sb.append(", mRedactTouchEvents=").append(mRedactTouchEvents); - sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight); - sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth); - sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]"); - sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]"); - sb.append('}'); - return sb.toString(); - } - - public KeyguardAnalyticsProtos.Session toProto() { - KeyguardAnalyticsProtos.Session proto = new KeyguardAnalyticsProtos.Session(); - proto.setStartTimestampMillis(mStartTimestampMillis); - proto.setDurationMillis(mEndTimestampMillis - mStartTimestampMillis); - proto.setBuild(Build.FINGERPRINT); - proto.setResult(mResult); - proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents); - proto.touchEvents = mMotionEvents.toArray(proto.touchEvents); - proto.setTouchAreaWidth(mTouchAreaWidth); - proto.setTouchAreaHeight(mTouchAreaHeight); - proto.setType(mType); - if (mRedactTouchEvents) { - redactTouchEvents(proto.touchEvents); - } - return proto; - } - - private void redactTouchEvents(TouchEvent[] touchEvents) { - for (int i = 0; i < touchEvents.length; i++) { - TouchEvent t = touchEvents[i]; - for (int j = 0; j < t.pointers.length; j++) { - TouchEvent.Pointer p = t.pointers[j]; - p.clearX(); - p.clearY(); - } - t.setRedacted(true); - } - } - - private SensorEvent protoFromSensorEvent(android.hardware.SensorEvent ev, long sysTimeNanos) { - SensorEvent proto = new SensorEvent(); - proto.setType(ev.sensor.getType()); - proto.setTimeOffsetNanos(sysTimeNanos - mStartSystemTimeNanos); - proto.setTimestamp(ev.timestamp); - proto.values = ev.values.clone(); - return proto; - } - - private TouchEvent protoFromMotionEvent(MotionEvent ev) { - int count = ev.getPointerCount(); - TouchEvent proto = new TouchEvent(); - proto.setTimeOffsetNanos(ev.getEventTimeNano() - mStartSystemTimeNanos); - proto.setAction(ev.getActionMasked()); - proto.setActionIndex(ev.getActionIndex()); - proto.pointers = new TouchEvent.Pointer[count]; - for (int i = 0; i < count; i++) { - TouchEvent.Pointer p = new TouchEvent.Pointer(); - p.setX(ev.getX(i)); - p.setY(ev.getY(i)); - p.setSize(ev.getSize(i)); - p.setPressure(ev.getPressure(i)); - p.setId(ev.getPointerId(i)); - proto.pointers[i] = p; - if ((ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP && ev.getActionIndex() == i) - || ev.getActionMasked() == MotionEvent.ACTION_UP) { - p.boundingBox = mPointerTracker.getPointerBoundingBox(p.getId()); - p.setLength(mPointerTracker.getPointerLength(p.getId())); - } - } - if (ev.getActionMasked() == MotionEvent.ACTION_UP) { - proto.boundingBox = mPointerTracker.getBoundingBox(); - } - return proto; - } - - /** - * Discards the x / y coordinates of the touch events on serialization. Retained are the - * size of the individual and overall bounding boxes and the length of each pointer's gesture. - */ - public void setRedactTouchEvents() { - mRedactTouchEvents = true; - } - - public void setTouchArea(int width, int height) { - mTouchAreaWidth = width; - mTouchAreaHeight = height; - } - - public long getStartTimestampMillis() { - return mStartTimestampMillis; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto b/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto deleted file mode 100644 index 68b1590..0000000 --- a/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -syntax = "proto2"; - -package keyguard; - -option java_package = "com.android.keyguard.analytics"; -option java_outer_classname = "KeyguardAnalyticsProtos"; - -message Session { - message TouchEvent { - message BoundingBox { - optional float width = 1; - optional float height = 2; - } - - enum Action { - // Keep in sync with MotionEvent. - DOWN = 0; - UP = 1; - MOVE = 2; - CANCEL = 3; - OUTSIDE = 4; - POINTER_DOWN = 5; - POINTER_UP = 6; - } - - message Pointer { - optional float x = 1; - optional float y = 2; - optional float size = 3; - optional float pressure = 4; - optional int32 id = 5; - optional float length = 6; - // Bounding box of the pointer. Only set on UP or POINTER_UP event of this pointer. - optional BoundingBox boundingBox = 7; - } - - optional uint64 timeOffsetNanos = 1; - optional Action action = 2; - optional int32 actionIndex = 3; - repeated Pointer pointers = 4; - /* If true, the the x / y coordinates of the touch events were redacted. Retained are the - size of the individual and overall bounding boxes and the length of each pointer's - gesture. */ - optional bool redacted = 5; - // Bounding box of the whole gesture. Only set on UP event. - optional BoundingBox boundingBox = 6; - } - - message SensorEvent { - enum Type { - ACCELEROMETER = 1; - GYROSCOPE = 4; - LIGHT = 5; - PROXIMITY = 8; - ROTATION_VECTOR = 11; - } - - optional Type type = 1; - optional uint64 timeOffsetNanos = 2; - repeated float values = 3; - optional uint64 timestamp = 4; - } - - enum Result { - FAILURE = 0; - SUCCESS = 1; - UNKNOWN = 2; - } - - enum Type { - KEYGUARD_INSECURE = 0; - KEYGUARD_SECURE = 1; - RANDOM_WAKEUP = 2; - } - - optional uint64 startTimestampMillis = 1; - optional uint64 durationMillis = 2; - optional string build = 3; - optional Result result = 4; - repeated TouchEvent touchEvents = 5; - repeated SensorEvent sensorEvents = 6; - - optional int32 touchAreaWidth = 9; - optional int32 touchAreaHeight = 10; - optional Type type = 11; -} diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml index ba3c042..c89f9bf 100644 --- a/packages/PrintSpooler/res/values-km-rKH/strings.xml +++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml @@ -60,7 +60,7 @@ </plurals> <string name="cancel" msgid="4373674107267141885">"បោះបង់"</string> <string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើម​ឡើងវិញ"</string> - <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព​"</string> + <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព"</string> <string name="reason_unknown" msgid="5507940196503246139">"មិន​ស្គាល់"</string> <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិន​អាច​ប្រើ​បាន"</string> <string name="print_error_default_message" msgid="8568506918983980567">"មិន​អាច​បង្កើážâ€‹áž€áž¶ážšâ€‹áž„ារ​បោះពុម្ព"</string> diff --git a/packages/SettingsProvider/res/values-af/defaults.xml b/packages/SettingsProvider/res/values-af/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-af/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-am/defaults.xml b/packages/SettingsProvider/res/values-am/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-am/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-ar/defaults.xml b/packages/SettingsProvider/res/values-ar/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-ar/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-bg/defaults.xml b/packages/SettingsProvider/res/values-bg/defaults.xml new file mode 100644 index 0000000..1d546ff --- /dev/null +++ b/packages/SettingsProvider/res/values-bg/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%2$s от %1$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-ca/defaults.xml b/packages/SettingsProvider/res/values-ca/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-ca/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-cs/defaults.xml b/packages/SettingsProvider/res/values-cs/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-cs/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-da/defaults.xml b/packages/SettingsProvider/res/values-da/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-da/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-de/defaults.xml b/packages/SettingsProvider/res/values-de/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-de/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-el/defaults.xml b/packages/SettingsProvider/res/values-el/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-el/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-en-rGB/defaults.xml b/packages/SettingsProvider/res/values-en-rGB/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-en-rGB/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-en-rIN/defaults.xml b/packages/SettingsProvider/res/values-en-rIN/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-en-rIN/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-es-rUS/defaults.xml b/packages/SettingsProvider/res/values-es-rUS/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-es-rUS/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-es/defaults.xml b/packages/SettingsProvider/res/values-es/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-es/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-et-rEE/defaults.xml b/packages/SettingsProvider/res/values-et-rEE/defaults.xml new file mode 100644 index 0000000..5f99ed9 --- /dev/null +++ b/packages/SettingsProvider/res/values-et-rEE/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%2$s, %1$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-fa/defaults.xml b/packages/SettingsProvider/res/values-fa/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-fa/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-fi/defaults.xml b/packages/SettingsProvider/res/values-fi/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-fi/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-fr-rCA/defaults.xml b/packages/SettingsProvider/res/values-fr-rCA/defaults.xml new file mode 100644 index 0000000..1a04b0f --- /dev/null +++ b/packages/SettingsProvider/res/values-fr-rCA/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%2$s de %1$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-fr/defaults.xml b/packages/SettingsProvider/res/values-fr/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-fr/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-hi/defaults.xml b/packages/SettingsProvider/res/values-hi/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-hi/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-hr/defaults.xml b/packages/SettingsProvider/res/values-hr/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-hr/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-hu/defaults.xml b/packages/SettingsProvider/res/values-hu/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-hu/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-hy-rAM/defaults.xml b/packages/SettingsProvider/res/values-hy-rAM/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-hy-rAM/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-in/defaults.xml b/packages/SettingsProvider/res/values-in/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-in/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-it/defaults.xml b/packages/SettingsProvider/res/values-it/defaults.xml new file mode 100644 index 0000000..bc995b0 --- /dev/null +++ b/packages/SettingsProvider/res/values-it/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-iw/defaults.xml b/packages/SettingsProvider/res/values-iw/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-iw/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-ja/defaults.xml b/packages/SettingsProvider/res/values-ja/defaults.xml new file mode 100644 index 0000000..bc995b0 --- /dev/null +++ b/packages/SettingsProvider/res/values-ja/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-ka-rGE/defaults.xml b/packages/SettingsProvider/res/values-ka-rGE/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-ka-rGE/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-km-rKH/defaults.xml b/packages/SettingsProvider/res/values-km-rKH/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-km-rKH/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-ko/defaults.xml b/packages/SettingsProvider/res/values-ko/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-ko/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-lt/defaults.xml b/packages/SettingsProvider/res/values-lt/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-lt/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-lv/defaults.xml b/packages/SettingsProvider/res/values-lv/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-lv/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-ms-rMY/defaults.xml b/packages/SettingsProvider/res/values-ms-rMY/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-ms-rMY/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-nb/defaults.xml b/packages/SettingsProvider/res/values-nb/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-nb/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-nl/defaults.xml b/packages/SettingsProvider/res/values-nl/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-nl/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-pl/defaults.xml b/packages/SettingsProvider/res/values-pl/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-pl/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-pt-rPT/defaults.xml b/packages/SettingsProvider/res/values-pt-rPT/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-pt-rPT/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-pt/defaults.xml b/packages/SettingsProvider/res/values-pt/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-pt/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-ro/defaults.xml b/packages/SettingsProvider/res/values-ro/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-ro/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-ru/defaults.xml b/packages/SettingsProvider/res/values-ru/defaults.xml new file mode 100644 index 0000000..bc995b0 --- /dev/null +++ b/packages/SettingsProvider/res/values-ru/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-sk/defaults.xml b/packages/SettingsProvider/res/values-sk/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-sk/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-sl/defaults.xml b/packages/SettingsProvider/res/values-sl/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-sl/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-sr/defaults.xml b/packages/SettingsProvider/res/values-sr/defaults.xml new file mode 100644 index 0000000..bc995b0 --- /dev/null +++ b/packages/SettingsProvider/res/values-sr/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-sv/defaults.xml b/packages/SettingsProvider/res/values-sv/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-sv/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-sw/defaults.xml b/packages/SettingsProvider/res/values-sw/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-sw/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-th/defaults.xml b/packages/SettingsProvider/res/values-th/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-th/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-tl/defaults.xml b/packages/SettingsProvider/res/values-tl/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-tl/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-tr/defaults.xml b/packages/SettingsProvider/res/values-tr/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-tr/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-uk/defaults.xml b/packages/SettingsProvider/res/values-uk/defaults.xml new file mode 100644 index 0000000..8ca4583 --- /dev/null +++ b/packages/SettingsProvider/res/values-uk/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%2$s о %1$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-vi/defaults.xml b/packages/SettingsProvider/res/values-vi/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-vi/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-zh-rCN/defaults.xml b/packages/SettingsProvider/res/values-zh-rCN/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-zh-rCN/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-zh-rHK/defaults.xml b/packages/SettingsProvider/res/values-zh-rHK/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-zh-rHK/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-zh-rTW/defaults.xml b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values-zu/defaults.xml b/packages/SettingsProvider/res/values-zu/defaults.xml new file mode 100644 index 0000000..295b4f5 --- /dev/null +++ b/packages/SettingsProvider/res/values-zu/defaults.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright (c) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string> +</resources> diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index bf97fc0..0e025a9 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -192,4 +192,7 @@ <!-- Default for Settings.Global.DEVICE_NAME $1=BRAND $2=MODEL--> <string name="def_device_name">%1$s %2$s</string> + <!-- Default for Settings.Secure.WAKE_GESTURE_ENABLED --> + <bool name="def_wake_gesture_enabled">true</bool> + </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 286921e..c4a54b7 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -70,7 +70,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 103; + private static final int DATABASE_VERSION = 104; private Context mContext; private int mUserHandle; @@ -1660,6 +1660,23 @@ public class DatabaseHelper extends SQLiteOpenHelper { } upgradeVersion = 103; } + + if (upgradeVersion == 103) { + db.beginTransaction(); + SQLiteStatement stmt = null; + try { + stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)" + + " VALUES(?,?);"); + loadBooleanSetting(stmt, Settings.Secure.WAKE_GESTURE_ENABLED, + R.bool.def_wake_gesture_enabled); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + if (stmt != null) stmt.close(); + } + upgradeVersion = 104; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { @@ -2222,6 +2239,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS, R.bool.def_install_non_market_apps); + loadBooleanSetting(stmt, Settings.Secure.WAKE_GESTURE_ENABLED, + R.bool.def_wake_gesture_enabled); + } finally { if (stmt != null) stmt.close(); } diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index e43464c..c45254e 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Ligging <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Wekker gestel vir <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Maak paneel toe"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Meer tyd"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Minder tyd"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G-data gedeaktiveer"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G data gedeaktiveer"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobieldata gedeaktiveer"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Meer instellings"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"USB-verbinding"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Warmkol"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Kennisgewings"</string> <string name="recents_empty_message" msgid="2269156590813544104">"ONLANGS"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programinligting"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"soek"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Minder dringende kennisgewings hieronder"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tik weer om oop te maak"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Sleep op om te ontsluit"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Totdat jy dit afskakel"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Een minuut lank"</item> + <item quantity="other" msgid="6924190729213550991">"%d minute lank"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Een uur lank"</item> + <item quantity="other" msgid="5408537517529822157">"%d uur lank"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index ce3d14b..5c4d98b 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"ብሉቱዠ<xliff:g id="STATE">%s</xliff:g>á¢"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"አካባቢ <xliff:g id="STATE">%s</xliff:g>á¢"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ማንቂያ ለ<xliff:g id="TIME">%s</xliff:g> ተዋቅሯáˆá¢"</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G á‹áˆ‚ብ ቦá‹áŠ—áˆ"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G á‹áˆ‚ብ ቦá‹áŠ—áˆ"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"የተንቀሳቃሽ á‹áˆ‚ብ ቦá‹áŠ—áˆ"</string> @@ -207,6 +213,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"ተጨማሪ ቅንብሮች"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"በማገናኘት ላá‹"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"መገናኛ áŠáŒ¥á‰¥"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"የቅáˆá‰¥ ጊዜዎች"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"የመተáŒá‰ ሪያ መረጃ"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"áˆáˆáŒ"</string> @@ -226,4 +234,10 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"በጣሠአስቸካዠያáˆáˆ†áŠ‘ ማሳወቂያዎች ከታች"</string> <string name="notification_tap_again" msgid="7590196980943943842">"ለመáŠáˆá‰µ ዳáŒáˆ መታ ያድáˆáŒ‰"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"ለማስከáˆá‰µ ወደ ላዠያንሸራትቱ"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index c9ddc23..d7c5651 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"البلوتوث <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ØØ§Ù„Ø© الموقع: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"تم ضبط المنبه على <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"إغلاق Ø§Ù„Ù„ÙˆØØ©"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"وقت أكثر"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"وقت أقل"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"تم تعطيل بيانات شبكات الجيل الثاني والجيل الثالث"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"تم تعطيل بيانات شبكة الجيل الرابع"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"تم تعطيل بيانات الجوال"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"المزيد من الإعدادات"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"النطاق"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"نقطة اتصال"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"الإشعارات"</string> <string name="recents_empty_message" msgid="2269156590813544104">"الأخيرة"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"معلومات التطبيق"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"Ø¨ØØ«"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"الإشعارات الأقل Ø¥Ù„ØØ§ØÙ‹Ø§ أدناه"</string> <string name="notification_tap_again" msgid="7590196980943943842">"انقر مرة أخرى Ù„Ù„ÙØªØ"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"مرر سريعًا لأعلى لإلغاء القÙÙ„"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Ù„ØÙŠÙ† تعطيل هذا الإعداد"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"لمدة دقيقة ÙˆØ§ØØ¯Ø©"</item> + <item quantity="other" msgid="6924190729213550991">"â€Ù„مدة %d من الدقائق"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"لمدة ساعة ÙˆØ§ØØ¯Ø©"</item> + <item quantity="other" msgid="5408537517529822157">"â€Ù„مدة %d من الساعات"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index d396b8c..2405804 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"МеÑтоположението е <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Будилникът е навит за <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"ЗатварÑне на панела"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Повече време"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"По-малко време"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G данните Ñа деактивирани"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G данните Ñа деактивирани"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Мобилните данни Ñа деактивирани"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Още наÑтройки"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Тетъринг"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка за доÑтъп"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"ИзвеÑтиÑ"</string> <string name="recents_empty_message" msgid="2269156590813544104">"СКОРОШÐИ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° приложението"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"търÑене"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Ппоказване на по-малко Ñпешните извеÑÑ‚Ð¸Ñ Ð¿Ð¾-долу"</string> <string name="notification_tap_again" msgid="7590196980943943842">"ДокоÑнете отново, за да отворите"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Прекарайте пръÑÑ‚ нагоре, за да отключите"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Докато не изключите това"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"За една минута"</item> + <item quantity="other" msgid="6924190729213550991">"За %d минути"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"За един чаÑ"</item> + <item quantity="other" msgid="5408537517529822157">"За %d чаÑа"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 51790b5..982e0c6 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Ubicació: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarma establerta a les <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Tanca el tauler."</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Més temps"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menys temps"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dades 2G-3G desactivades"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dades 4G desactivades"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dades mòbils desactivades"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Més opcions"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Ancoratge a xarxa"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificacions"</string> <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informació de l\'aplicació"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string> @@ -228,4 +232,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificacions menys urgents a continuació"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Torna a tocar per obrir-la."</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Fes lliscar el dit cap amunt per desbloquejar el teclat."</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Fins que no ho desactivis"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Durant un minut"</item> + <item quantity="other" msgid="6924190729213550991">"Durant %d minuts"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Durant una hora"</item> + <item quantity="other" msgid="5408537517529822157">"Durant %d hores"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 91b8f6e..570e1bb 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Poloha: <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"BudÃk je nastaven na <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"ZavÅ™Ãt panel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Delšà doba"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kratšà doba"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datové pÅ™enosy 2G a 3G jsou zakázány"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datové pÅ™enosy 4G jsou zakázány"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilnà data jsou zakázána"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Dalšà nastavenÃ"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"SdÃlenà datového pÅ™ipojenÃ"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"OznámenÃ"</string> <string name="recents_empty_message" msgid="2269156590813544104">"POSLEDNÃ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informace o aplikaci"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string> @@ -228,4 +232,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"MénÄ› urgentnà oznámenà nÞe"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Oznámenà otevÅ™ete opÄ›tovným klepnutÃm"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"ZaÅ™Ãzenà odemknete pÅ™ejetÃm prstem nahoru"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Dokud tuto funkci nevypnete"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Na jednu minutu"</item> + <item quantity="other" msgid="6924190729213550991">"Na %d min"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Na jednu hodinu"</item> + <item quantity="other" msgid="5408537517529822157">"Na %d h"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 1d1ca0a..127192a 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Placering <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmen er indstillet til <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Luk panelet"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mere tid"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mindre tid"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G-data er deaktiveret"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-data er deaktiveret"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobildata er deaktiveret"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Flere indstillinger"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Netdeling"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Underretninger"</string> <string name="recents_empty_message" msgid="2269156590813544104">"SENESTE"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Oplysninger om applikationen"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"søg"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre presserende underretninger nedenfor"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tryk igen for at Ã¥bne"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Stryg for at lÃ¥se op"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Indtil du slÃ¥r denne indstilling fra"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"I ét minut"</item> + <item quantity="other" msgid="6924190729213550991">"I %d minutter"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"I én time"</item> + <item quantity="other" msgid="5408537517529822157">"I %d timer"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index a79193d..c9c7b8e 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Standort <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Wecker gestellt für <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fenster schließen"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mehr Zeit"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Weniger Zeit"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-/3G-Daten deaktiviert"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-Daten deaktiviert"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilfunk Daten deaktiviert"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Weitere Einstellungen"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Benachrichtigungen"</string> <string name="recents_empty_message" msgid="2269156590813544104">"Letzte"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-Info"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string> @@ -228,4 +232,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Weniger dringende Benachrichtigungen unten"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Erneut tippen, um Benachrichtigung zu öffnen"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Zum Entsperren nach oben wischen"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Bis zur Deaktivierung"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Für eine Minute"</item> + <item quantity="other" msgid="6924190729213550991">"Für %d Minuten"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Für eine Stunde"</item> + <item quantity="other" msgid="5408537517529822157">"Für %d Stunden"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 32b7090..1d90dae 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Τοποθεσία <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Το ξυπνητήÏι Îχει οÏιστεί στις <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Κλείσιμο παÏαθÏÏου"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"ΠεÏισσότεÏος χÏόνος"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ΛιγότεÏος χÏόνος"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Τα δεδομÎνα 2G-3G απενεÏγοποιήθηκαν"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Τα δεδομÎνα 4G απενεÏγοποιήθηκαν"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Τα δεδομÎνα κινητής τηλεφωνίας απενεÏγοποιήθηκαν"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"ΠεÏισσότεÏες Ïυθμίσεις"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Î Ïόσδεση"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Σημείο Ï€Ïόσβασης Wi-Fi"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ειδοποιήσεις"</string> <string name="recents_empty_message" msgid="2269156590813544104">"ΠΡΟΣΦΑΤΑ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"ΠληÏοφοÏίες εφαÏμογής"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string> @@ -228,4 +232,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"ΛιγότεÏο επείγουσες ειδοποιήσεις παÏακάτω"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Πατήστε ξανά για να ανοίξετε"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"ΣÏÏετε για να ξεκλειδώσετε"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"ΜÎχÏι να το απενεÏγοποιήσετε"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Για Îνα λεπτό"</item> + <item quantity="other" msgid="6924190729213550991">"Για %d λεπτά"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Για μία ÏŽÏα"</item> + <item quantity="other" msgid="5408537517529822157">"Για %d ÏŽÏες"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index d26cfae..a9c6678 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Location <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm set for <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Close panel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"More time"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Less time"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G data disabled"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G data disabled"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobile data disabled"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"More settings"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string> <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"For one minute"</item> + <item quantity="other" msgid="6924190729213550991">"For %d minutes"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"For one hour"</item> + <item quantity="other" msgid="5408537517529822157">"For %d hours"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index d48e627..a9c6678 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Location <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm set for <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Close panel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"More time"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Less time"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G data disabled"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G data disabled"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobile data disabled"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"More settings"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string> <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string> @@ -223,8 +227,16 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"%d more"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"For one minute"</item> + <item quantity="other" msgid="6924190729213550991">"For %d minutes"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"For one hour"</item> + <item quantity="other" msgid="5408537517529822157">"For %d hours"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 23bcea2..6ca4ebc 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Ubicación <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarma: <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Cerrar panel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Más tiempo"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tiempo"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datos de 2G-3G inhabilitados"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datos de 4G inhabilitados"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Se inhabilitaron los datos móviles"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Más configuraciones"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Anclaje a red"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string> <string name="recents_empty_message" msgid="2269156590813544104">"RECIENTES"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string> @@ -225,8 +229,16 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"%d más"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificaciones menos urgentes abajo"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Presionar de nuevo para abrir"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar el dedo hacia arriba para desbloquear"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Hasta que lo desactives"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item> + <item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Durante una hora"</item> + <item quantity="other" msgid="5408537517529822157">"Durante %d horas"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 3bded8b..d0e9c0a 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Ubicación <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"La alarma sonará a la(s) <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Cerrar panel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Más tiempo"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tiempo"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datos 2G-3G inhabilitados"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datos 4G inhabilitados"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Datos móviles inhabilitados"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Más opciones"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Anclaje a red"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string> <string name="recents_empty_message" msgid="2269156590813544104">"RECIENTES"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificaciones menos urgente abajo"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Toca de nuevo para abrir"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Desliza el dedo hacia arriba para desbloquear"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Hasta apagar el dispositivo"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item> + <item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Durante una hora"</item> + <item quantity="other" msgid="5408537517529822157">"Durante %d horas"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml index 1979c55..c692b28 100644 --- a/packages/SystemUI/res/values-et-rEE/strings.xml +++ b/packages/SystemUI/res/values-et-rEE/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Asukoht: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Määratud äratus: <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Sule paneel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Rohkem aega"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Vähem aega"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G–3G andmeside keelatud"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G andmeside keelatud"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobiilne andmeside keelatud"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Rohkem seadeid"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Jagamine"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Leviala"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Märguanded"</string> <string name="recents_empty_message" msgid="2269156590813544104">"HILJUTISED"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Rakenduste teave"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string> @@ -224,4 +228,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Vähem kiireloomulised märguanded on allpool"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Avamiseks puudutage uuesti"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Lukustuse tühistamiseks pühkige üles"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Kuni lülitate selle välja"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Üheks minutiks"</item> + <item quantity="other" msgid="6924190729213550991">"%d minutiks"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Üheks tunniks"</item> + <item quantity="other" msgid="5408537517529822157">"%d tunniks"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 399740a..ee2c48d 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"بلوتوث <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"مکان <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"هشدار برای <xliff:g id="TIME">%s</xliff:g> تنظیم شد."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"بستن پانل"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"زمان بیشتر"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"زمان کمتر"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"â€Ø¯Ø§Ø¯Ù‡ 2G-3G ØºÛŒØ±ÙØ¹Ø§Ù„ شد"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"â€Ø¯Ø§Ø¯Ù‡ 4G غیر ÙØ¹Ø§Ù„ شد"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"داده‌های تلÙÙ† همراه ØºÛŒØ±ÙØ¹Ø§Ù„ است"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"تنظیمات بیشتر"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"اتصال به اینترنت با تلÙÙ† همراه"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"نقطه اتصال"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"اعلان‌ها"</string> <string name="recents_empty_message" msgid="2269156590813544104">"موارد اخیر"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"اطلاعات برنامه"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"جستجو"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"اعلان‌های کمتر Ùوری در زیر"</string> <string name="notification_tap_again" msgid="7590196980943943842">"برای باز کردن دوباره ضربه بزنید"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"برای باز کردن Ù‚ÙÙ„ سریع به بالا بکشید"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"تا وقتی آن را خاموش کنید"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"برای یک دقیقه"</item> + <item quantity="other" msgid="6924190729213550991">"â€Ø¨Ø±Ø§ÛŒ %d دقیقه"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"برای یک ساعت"</item> + <item quantity="other" msgid="5408537517529822157">"â€Ø¨Ø±Ø§ÛŒ %d ساعت"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index be7eb9b..44d35b3 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Sijainti <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Hälytys asetettu, aika: <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Sulje paneeli"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lisää aikaa"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Vähennä aikaa"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G-tiedonsiirto pois käytöstä"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-tiedonsiirto pois käytöstä"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobiilitiedonsiirto pois käytöstä"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Lisäasetukset"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Jaettu yhteys"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ilmoitukset"</string> <string name="recents_empty_message" msgid="2269156590813544104">"VIIMEISIMMÄT"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Sovellustiedot"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Vähemmän kiireelliset ilmoitukset ovat alla"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Avaa napauttamalla uudelleen"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Avaa lukitus pyyhkäisemällä ylös"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Kunnes poistat tämän käytöstä"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Minuutiksi"</item> + <item quantity="other" msgid="6924190729213550991">"%d minuutiksi"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Tunniksi"</item> + <item quantity="other" msgid="5408537517529822157">"%d tunniksi"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 390ec81..d9790f6 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -158,6 +158,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth : <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localisation <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme réglée sur <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Données 2G-3G désactivées"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Données 4G désactivées"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Données mobiles désactivées"</string> @@ -209,6 +215,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Plus de paramètres"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Partage de connexion"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Point d\'accès sans fil"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"RÉCENTS"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Détails de l\'application"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string> @@ -228,4 +236,10 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Notifications moins urgentes affichées ci-dessous"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Touchez à nouveau pour ouvrir"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Glissez vers le haut pour déverrouiller"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index a0c5cfe..d95fe06 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -158,6 +158,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth : <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localisation <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme réglée sur <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Données 2G-3G désactivées"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Données 4G désactivées"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Données mobiles désactivées"</string> @@ -209,6 +215,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Plus de paramètres"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Partage de connexion"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Point d\'accès"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"RÉCENTS"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informations sur l\'application"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string> @@ -228,4 +236,10 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Notifications moins urgentes ci-dessous"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Appuyer à nouveau pour ouvrir"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Faire glisser pour déverrouiller"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 6850b3d..60d7de1 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"सà¥à¤¥à¤¾à¤¨ <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"<xliff:g id="TIME">%s</xliff:g> के लिठअलारà¥à¤® सेट किया गया."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"फलक बंद करें"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"अधिक समय"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"कम समय"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G डेटा अकà¥à¤·à¤®"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G डेटा अकà¥à¤·à¤®"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"मोबाइल डेटा अकà¥à¤·à¤®"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"और सेटिंग"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"टेदरिंग"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"हॉटसà¥à¤ªà¥‰à¤Ÿ"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"सूचनाà¤à¤‚"</string> <string name="recents_empty_message" msgid="2269156590813544104">"हाल ही का"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"à¤à¤ªà¥â€à¤²à¤¿à¤•ेशन जानकारी"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"खोज"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"कम अतà¥à¤¯à¤¾à¤µà¤¶à¥à¤¯à¤• सूचनाà¤à¤‚ नीचे दी गई हैं"</string> <string name="notification_tap_again" msgid="7590196980943943842">"खोलने के लिठपà¥à¤¨: टैप करें"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"अनलॉक करने के लिठऊपर सà¥à¤µà¤¾à¤‡à¤ª करें"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"जब तक आप इसे बंद नहीं कर देते"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"à¤à¤• मिनट के लिà¤"</item> + <item quantity="other" msgid="6924190729213550991">"%d मिनट के लिà¤"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"à¤à¤• घंटे के लिà¤"</item> + <item quantity="other" msgid="5408537517529822157">"%d घंटे के लिà¤"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index b00c382..09dde42 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth – <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokacija <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Vrijeme alarma: <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zatvori ploÄu"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"ViÅ¡e vremena"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Manje vremena"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Onemogućeni su 2G-3G podaci"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Onemogućeni su 4G podaci"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Onemogućeni su mobilni podaci"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"ViÅ¡e postavki"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Dijeljenje veze"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ŽariÅ¡na toÄka"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavijesti"</string> <string name="recents_empty_message" msgid="2269156590813544104">"NEDAVNO"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Manje hitne obavijesti pri dnu"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Dodirnite opet za otvaranje"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"PrijeÄ‘ite prstom prema gore za otkljuÄavanje"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Dok ne iskljuÄite"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Jednu minutu"</item> + <item quantity="other" msgid="6924190729213550991">"%d min"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Jedan sat"</item> + <item quantity="other" msgid="5408537517529822157">"%d h"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index b75c7f3..dc85416 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Helyadatok: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Ébresztés idÅ‘pontja: <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Panel bezárása"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Több idÅ‘"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kevesebb idÅ‘"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G adatforgalom letiltva"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G adatforgalom letiltva"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobil adatforgalom letiltva"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"További beállÃtások"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Megosztás"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"ÉrtesÃtések"</string> <string name="recents_empty_message" msgid="2269156590813544104">"LEGUTÓBBIAK"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Az alkalmazás adatai"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"keresés"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"A kevésbé sürgÅ‘s értesÃtések lentebb vannak"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Koppintson rá ismét a megnyitáshoz"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Húzza felfelé az ujját a feloldáshoz"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"AmÃg ki nem kapcsolja ezt"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Egy percen át"</item> + <item quantity="other" msgid="6924190729213550991">"%d percen át"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Egy órán át"</item> + <item quantity="other" msgid="5408537517529822157">"%d órán át"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index 904b998..b11a004 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth-Õ¨ <xliff:g id="STATE">%s</xliff:g> Õ§:"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ÕÕ¥Õ²Õ¡Õ¤Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨Õ <xliff:g id="STATE">%s</xliff:g>:"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Ô¶Õ¡Ö€Õ©Õ¸Ö‚ÖÕ«Õ¹Õ¨ Õ¤Ö€Õ¾Õ¡Õ® Õ§ <xliff:g id="TIME">%s</xliff:g>-Õ«Õ¶:"</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Õ¡Õ¶Õ»Õ¡Õ¿Õ¾Õ¡Õ® Õ¥Õ¶"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Õ¡Õ¶Õ»Õ¡Õ¿Õ¾Õ¡Õ® Õ¥Õ¶"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Õ‡Õ¡Ö€ÕªÕ¡Õ¯Õ¡Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¨ Õ¡Õ¶Õ»Õ¡Õ¿Õ¾Õ¡Õ® Õ¥Õ¶"</string> @@ -207,6 +213,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Õ€Õ¡Õ¾Õ¥Õ¬ÕµÕ¡Õ¬ Õ¯Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Õ„Õ«Õ¡ÖÕ¸Ö‚Õ´"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Ô¹Õ¥Õª Õ¯Õ¥Õ¿"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"ÕŽÔµÕÕ‹Ô»Õ†Õ†ÔµÕÔ¸"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Õ€Õ¡Õ¾Õ¥Õ¬Õ¾Õ¡Õ®Õ« Õ´Õ¡Õ½Õ«Õ¶"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"Õ¸Ö€Õ¸Õ¶Õ¥Õ¬"</string> @@ -226,4 +234,10 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"ÕŠÕ¡Õ¯Õ¡Õ½ Õ°Ö€Õ¡Õ¿Õ¡Õº Õ®Õ¡Õ¶Õ¸Ö‚ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨ Õ½Õ¿Õ¸Ö€Ö‡"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Ô¿Ö€Õ¯Õ«Õ¶ Õ°ÕºÕ¥Ö„Õ Õ¢Õ¡ÖÕ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"ÕÕ¡Õ°Õ¥ÖÖ€Õ¥Ö„ Õ¾Õ¥Ö€Ö‡` Õ¡ÕºÕ¡Õ¯Õ¸Õ²ÕºÕ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 02313b3..f62209f 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokasi <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm disetel ke <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Tutup panel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lebih lama"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Lebih cepat"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data 2G-3G dinonaktifkan"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data 4G dinonaktifkan"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Data seluler dinonaktifkan"</string> @@ -202,15 +205,12 @@ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Layar Transmisi"</string> <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATIS"</string> - <!-- no translation found for quick_settings_inversion_label (8790919884718619648) --> - <skip /> + <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inversi warna"</string> <string name="quick_settings_color_space_label" msgid="853443689745584770">"Mode koreksi warna"</string> - <!-- no translation found for quick_settings_more_settings (326112621462813682) --> - <skip /> - <!-- no translation found for quick_settings_tethering_label (7153452060448575549) --> - <skip /> - <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) --> - <skip /> + <string name="quick_settings_more_settings" msgid="326112621462813682">"Setelan lainnya"</string> + <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Menambatkan"</string> + <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pemberitahuan"</string> <string name="recents_empty_message" msgid="2269156590813544104">"TERBARU"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Info Aplikasi"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string> @@ -227,8 +227,16 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"%d lainnya"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"Pemberitahuan kurang darurat di bawah"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Ketuk lagi untuk membuka"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Gesek ke atas untuk membuka kunci"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Hingga Anda menonaktifkan ini"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Selama satu menit"</item> + <item quantity="other" msgid="6924190729213550991">"Selama %d menit"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Selama satu jam"</item> + <item quantity="other" msgid="5408537517529822157">"Selama %d jam"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 412d001..6cf13be 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Posizione: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Allarme impostato per: <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Chiudi riquadro"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Più tempo"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Meno tempo"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dati 2G-3G disattivati"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dati 4G disattivati"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dati mobili disattivati"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Altre impostazioni"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifiche"</string> <string name="recents_empty_message" msgid="2269156590813544104">"MESSAGGI RECENTI"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informazioni sull\'applicazione"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string> @@ -228,4 +232,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Notifiche meno urgenti in basso"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tocca ancora per aprire"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Scorri verso l\'alto per sbloccare"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Fino alla disattivazione"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Per un minuto"</item> + <item quantity="other" msgid="6924190729213550991">"Per %d minuti"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Per un\'ora"</item> + <item quantity="other" msgid="5408537517529822157">"Per %d ore"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 33a6701..4f07006 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"â€Bluetooth â€<xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"×”×ž×™×§×•× <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ההתר××” × ×§×‘×¢×” ל-<xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"סגור ×—×œ×•× ×™×ª"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"יותר זמן"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"פחות זמן"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"â€× ×ª×•× ×™ 2G-3G מושבתי×"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"â€× ×ª×•× ×™ 4G מושבתי×"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"× ×ª×•× ×™× ×œ× ×™×™×“ מושבתי×"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"הגדרות × ×•×¡×¤×•×ª"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"שיתוף ××™× ×˜×¨× ×˜ בין × ×™×™×“×™×"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"× ×§×•×“×” לשיתוף ××™× ×˜×¨× ×˜"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"הודעות"</string> <string name="recents_empty_message" msgid="2269156590813544104">"××—×¨×•× ×™×"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"מידע על ×”×פליקציה"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"חפש"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"הודעות בדחיפות × ×ž×•×›×” יותר בהמשך"</string> <string name="notification_tap_again" msgid="7590196980943943842">"הקש שוב כדי לפתוח"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"החלק מעלה כדי לבטל ×ת ×”× ×¢×™×œ×”"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"עד שתכבה"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"למשך דקה ×חת"</item> + <item quantity="other" msgid="6924190729213550991">"â€×œ×ž×©×š %d דקות"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"למשך שעה ×חת"</item> + <item quantity="other" msgid="5408537517529822157">"â€×œ×ž×©×š %d שעות"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 3ed729b..84bbf8b 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -158,6 +158,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ç¾åœ¨åœ°: <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"アラームã¯<xliff:g id="TIME">%s</xliff:g>ã«è¨å®šã•れã¦ã„ã¾ã™ã€‚"</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G~3GデータãŒç„¡åйã«ãªã‚Šã¾ã—ãŸ"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4GデータãŒç„¡åйã«ãªã‚Šã¾ã—ãŸ"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"モãƒã‚¤ãƒ«ãƒ‡ãƒ¼ã‚¿ãŒç„¡åйã«ãªã‚Šã¾ã—ãŸ"</string> @@ -209,6 +215,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"詳細è¨å®š"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"テザリング"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"アクセスãƒã‚¤ãƒ³ãƒˆ"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"最近"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"ã‚¢ãƒ—ãƒªæƒ…å ±"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"検索"</string> @@ -228,4 +236,10 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"緊急度ã®ä½Žã„通知を下ã«è¡¨ç¤º"</string> <string name="notification_tap_again" msgid="7590196980943943842">"é–‹ãã«ã¯ã‚‚ã†ä¸€åº¦ã‚¿ãƒƒãƒ—ã—ã¦ãã ã•ã„"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"ãƒãƒƒã‚¯è§£é™¤ã™ã‚‹ã«ã¯ä¸Šã«ã‚¹ãƒ¯ã‚¤ãƒ—ã—ã¦ãã ã•ã„"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index 506ec3d..c31ea24 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"მდებáƒáƒ ეáƒáƒ‘რ<xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"მáƒáƒ¦áƒ•იძáƒáƒ რდáƒáƒ§áƒ”ნებულიáƒ: <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G ინტერნეტი გáƒáƒ˜áƒ—იშáƒ."</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G მáƒáƒœáƒáƒªáƒ”მები გáƒáƒ—იშულიáƒ"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"მáƒáƒ‘ილური ინტერნეტი გáƒáƒ˜áƒ—იშáƒ."</string> @@ -202,11 +208,13 @@ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string> <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"გáƒáƒœáƒáƒ—ებáƒ"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"áƒáƒ•ტáƒáƒ›áƒáƒ¢áƒ£áƒ áƒáƒ“"</string> - <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ინვერტირებáƒ"</string> + <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ფერების შებრუნებáƒ"</string> <string name="quick_settings_color_space_label" msgid="853443689745584770">"ფერთრკáƒáƒ ექციის რეჟიმი"</string> <string name="quick_settings_more_settings" msgid="326112621462813682">"დáƒáƒ›áƒáƒ¢áƒ”ბითი პáƒáƒ áƒáƒ›áƒ”ტრები"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"მáƒáƒ“ემის რეჟიმი"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"წვდáƒáƒ›áƒ˜áƒ¡ წერტილი"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"ბáƒáƒšáƒ დრáƒáƒ˜áƒ¡"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"áƒáƒžáƒšáƒ˜áƒ™áƒáƒªáƒ˜áƒ˜áƒ¡ შესáƒáƒ®áƒ”ბ"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"ძიებáƒ"</string> @@ -223,8 +231,13 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"%d სხვáƒ"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"ქვემáƒáƒ— მითითებულირნáƒáƒ™áƒšáƒ”ბáƒáƒ“ სáƒáƒ¡áƒ¬áƒ áƒáƒ¤áƒ შეტყáƒáƒ‘ინებები"</string> <string name="notification_tap_again" msgid="7590196980943943842">"შეეხეთ ისევ გáƒáƒ¡áƒáƒ®áƒ¡áƒœáƒ”ლáƒáƒ“"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"გáƒáƒáƒ¡áƒ იáƒáƒšáƒ”თ ზევით გáƒáƒœáƒ¡áƒáƒ‘ლáƒáƒ™áƒáƒ“"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml index f3b4e9e..a9cf7d6 100644 --- a/packages/SystemUI/res/values-km-rKH/strings.xml +++ b/packages/SystemUI/res/values-km-rKH/strings.xml @@ -64,7 +64,7 @@ <string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុង​រក្សាទុក​រូបážážâ€‹áž¢áŸáž€áŸ’រង់…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុង​រក្សាទុក​រូបážážâ€‹áž¢áŸáž€áŸ’រង់..."</string> <string name="screenshot_saving_text" msgid="2419718443411738818">"រូបážážâ€‹áž¢áŸáž€áŸ’រង់​កំពុង​ážáŸ’រូវ​បាន​រក្សាទុក។"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបážážâ€‹áž¢áŸáž€áŸ’រង់។​"</string> + <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបážážâ€‹áž¢áŸáž€áŸ’រង់។"</string> <string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ​ដើម្បី​មើល​រូបážážâ€‹áž¢áŸáž€áŸ’រង់​របស់​អ្នក​។"</string> <string name="screenshot_failed_title" msgid="705781116746922771">"មិន​អាច​ចាប់​យក​រូប​ážážâ€‹áž¢áŸáž€áŸ’រង់​។"</string> <string name="screenshot_failed_text" msgid="8134011269572415402">"មិន​អាច​រក្សាទុក​រូបážážâ€‹áž¢áŸáž€áŸ’រង់​។ ឧបករណáŸâ€‹áž•្ទុក​អាច​កំពុង​ប្រើ​​។"</string> @@ -140,7 +140,7 @@ <string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាážâ€‹áž€áž¶ážšâ€‹áž‡áž¼áž“​ដំណឹង។"</string> <string name="accessibility_gps_enabled" msgid="3511469499240123019">"បាន​បើក GPS ។"</string> <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល​​ GPS ។"</string> - <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលáŸážâ€‹"</string> + <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលáŸáž"</string> <string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធី​រោទáŸâ€‹áž‰áŸážšáŸ”"</string> <string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធី​រោទáŸâ€‹ážŸáŸ’ងាážáŸ‹áŸ”"</string> <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសáŸáž’។"</string> @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"ប៊្លូធូស <xliff:g id="STATE">%s</xliff:g> ។"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ទីážáž¶áŸ†áž„ <xliff:g id="STATE">%s</xliff:g> ។"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"កំណážáŸ‹â€‹ážŸáŸ†áž¡áŸáž„​រោទáŸâ€‹ážŸáž˜áŸ’រាប់ <xliff:g id="TIME">%s</xliff:g> ។"</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"បិទ​បន្ទះ"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"áž–áŸáž›â€‹áž…្រើនជាង"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"áž–áŸáž›â€‹ážáž·áž…ជាង"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"បាន​បិទ​ទិន្ននáŸáž™ 2G-3G"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"បាន​បិទ​ទិន្ននáŸáž™ 4G"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"បាន​បិទ​ទិន្ននáŸáž™â€‹áž…áž›áŸáž"</string> @@ -187,7 +190,7 @@ <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ចាក់​សោ​​បញ្ឈរ"</string> <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ចាក់​សោ​​​ផ្ដáŸáž€"</string> <string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ážáŸ’រ​បញ្ចូល"</string> - <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​ážáž¶áŸ†áž„​"</string> + <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​ážáž¶áŸ†áž„"</string> <string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីážáž¶áŸ†áž„​បាន​បិទ"</string> <string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណáŸâ€‹áž˜áŸážŒáŸ€"</string> <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string> @@ -207,10 +210,11 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"ការ​កំណážáŸ‹â€‹áž…្រើន​ទៀáž"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"ការ​ភ្ជាប់"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"áž ážážŸáŸ’ប៉áž"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"ការ​ជូនដំណឹង"</string> <string name="recents_empty_message" msgid="2269156590813544104">"ážáŸ’មីៗ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"áž–áŸážáŸŒáž˜áž¶áž“​កម្មវិធី"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string> - <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nážáŸ’រូវ​បាន​ážáŸ’ážšáž½ážáž–ិនិážáŸ’យ​"</string> + <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nážáŸ’រូវ​បាន​ážáŸ’ážšáž½ážáž–ិនិážáŸ’áž™"</string> <string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string> <string name="description_direction_up" msgid="7169032478259485180">"រុញ​ឡើង​លើ​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string> <string name="description_direction_left" msgid="7207478719805562165">"រុញ​ទៅ​ឆ្វáŸáž„​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"ការ​ជូន​ដំណឹង​​មិន​សូវ​បន្ទាន់​ážáž¶áž„​ក្រោម"</string> <string name="notification_tap_again" msgid="7590196980943943842">"ប៉ះ​ម្ដង​ទៀហដើម្បី​បើក"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"អូស​ឡើង​លើ ដើម្បី​ដោះ​សោ"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"ážšáž áž¼ážâ€‹ážŠáž›áŸ‹áž–áŸáž›â€‹â€‹áž¢áŸ’នក​បិទ​វា"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"សម្រាប់​មួយ​នាទី"</item> + <item quantity="other" msgid="6924190729213550991">"សម្រាប់ %d នាទី"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"សម្រាប់​មួយ​ម៉ោង"</item> + <item quantity="other" msgid="5408537517529822157">"សម្រាប់ %d ម៉ោង"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 2b39e14..bb2e5c1 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"블루투스 <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"위치 <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ì•ŒëžŒì´ <xliff:g id="TIME">%s</xliff:g>(으)로 ì„¤ì •ë˜ì—ˆìŠµë‹ˆë‹¤."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"íŒ¨ë„ ë‹«ê¸°"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"시간 늘리기"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"시간 줄ì´ê¸°"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G ë°ì´í„° 사용중지ë¨"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G ë°ì´í„° 사용중지ë¨"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"ëª¨ë°”ì¼ ë°ì´í„° 사용중지ë¨"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"ì„¤ì • ë”보기"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"í…Œë”ë§"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"핫스팟"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"알림"</string> <string name="recents_empty_message" msgid="2269156590813544104">"최근"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"ì• í”Œë¦¬ì¼€ì´ì…˜ ì •ë³´"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"검색"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"ì•„ëž˜ì— ëœ ê¸‰í•œ 알림 표시"</string> <string name="notification_tap_again" msgid="7590196980943943842">"다시 íƒí•˜ì—¬ 열기"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"위로 스와ì´í”„하여 ìž ê¸ˆ í•´ì œ"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"ì´ ê¸°ëŠ¥ì„ ì‚¬ìš© ì¤‘ì§€í• ë•Œê¹Œì§€"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"1ë¶„ ë™ì•ˆ"</item> + <item quantity="other" msgid="6924190729213550991">"%dë¶„ ë™ì•ˆ"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"1시간 ë™ì•ˆ"</item> + <item quantity="other" msgid="5408537517529822157">"%d시간 ë™ì•ˆ"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml index 5d2ccbc..7220777 100644 --- a/packages/SystemUI/res/values-lo-rLA/strings.xml +++ b/packages/SystemUI/res/values-lo-rLA/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ສະ​ຖານ​ທີ່ <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ຕັ້ງໂມງປຸຠ<xliff:g id="TIME">%s</xliff:g> à»àº¥à»‰àº§."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"​ປິດ​à»àº–ບ"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"​ເພີ່ມ​ເວ​ລາ"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ຫຼຸດ​ເວ​ລາ"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"àºàº´àº™à»€àº•ີເນັດ 2G​, 3G ຖືàºàº›àº´àº”à»àº¥à»‰àº§"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"àºàº²àº™àº™àº³à»ƒàºŠà»‰àº‚à»à»‰àº¡àº¹àº™ 4G ຖືàºàº›àº´àº”à»àº¥à»‰àº§"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"àºàº´àº™à»€àº•ີເນັດໃນມືຖືຖືàºàº›àº´àº”àºàº²àº™àº™àº³à»ƒàºŠà»‰à»àº¥à»‰àº§"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"​àºàº²àº™â€‹àº•ັ້ງ​ຄ່າ​ເພີ່ມ​ເຕີມ"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"​àºàº²àº™â€‹àº›à»ˆàºàº™â€‹àºªàº±àº™â€‹àºàº²àº™"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"​ຮັàºàº”​ສະ​ປàºàº”"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"àºàº²àº™à»àºˆà»‰àº‡à»€àº•ືàºàº™"</string> <string name="recents_empty_message" msgid="2269156590813544104">"ບà»à»ˆâ€‹àº”ົນ​ມາ​ນີ້"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"​ຂà»à»‰â€‹àº¡àº¹àº™â€‹à»àºàº±àºšâ€‹àºžâ€‹àº¥àº´â€‹à»€àº„​ຊັນ"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"ຊàºàºàº«àº²"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"àºàº²àº™â€‹à»àºˆà»‰àº‡à»€àº•ືàºàº™â€‹àº—ີ່​ສຳຄັນ​ໜ້àºàºâ€‹àºàº§à»ˆàº²â€‹àº¢àº¹à»ˆâ€‹àº”້ານ​ລຸ່ມ"</string> <string name="notification_tap_again" msgid="7590196980943943842">"à»àº•ະ​àºàºµàºâ€‹àº„ັ້ງ​ເພື່àºâ€‹à»€àº›àºµàº”"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"ເລື່àºàº™â€‹àº‚ຶ້ນ​ເພື່àºâ€‹àº›àº»àº”​ລັàºàº"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນàºàº§à»ˆàº²â€‹àº—່ານ​ຈະ​ປິດ"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"​ສຳ​ລັບ​ນຶ່ງ​ນາ​ທີ"</item> + <item quantity="other" msgid="6924190729213550991">"ສຳ​ລັບ %d ນາ​ທີ"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"​ສຳ​ລັບນຶ່ງ​ຊົ່ວ​ໂມງ"</item> + <item quantity="other" msgid="5408537517529822157">"ສຳ​ລັບ %d ຊົ່ວ​ໂມງ"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index cede14c..d0ebd54 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"„Bluetooth“ <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"VietovÄ— – <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Signalas nustatytas <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Uždaryti skydelį"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Daugiau laiko"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mažiau laiko"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G–3G duomenys neleidžiami"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G duomenys neleidžiami"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilieji duomenys neleidžiami"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Daugiau nustatymų"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Susiejimas"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"VieÅ¡osios interneto prieigos taÅ¡kas"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"PraneÅ¡imai"</string> <string name="recents_empty_message" msgid="2269156590813544104">"PASTARIEJI"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Programos informacija"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"paieÅ¡ka"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Mažiau skubÅ«s praneÅ¡imai toliau"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Palieskite dar kartÄ…, kad atidarytumÄ—te"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Perbraukite aukÅ¡tyn, kad atrakintumÄ—te"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Kol iÅ¡jungsite"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"1 min."</item> + <item quantity="other" msgid="6924190729213550991">"%d min."</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"1 val."</item> + <item quantity="other" msgid="5408537517529822157">"%d val."</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 280ce40..23ce4a9 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth statuss: <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"AtraÅ¡anÄs vieta: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"SignÄls ir iestatÄ«ts uz: <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"AizvÄ“rt paneli"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"IlgÄk"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"MazÄks laiks"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G–3G dati atspÄ“joti"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G dati atspÄ“joti"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilie dati atspÄ“joti"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"VairÄk iestatÄ«jumu"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Piesaiste"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"TÄ«klÄjs"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Paziņojumi"</string> <string name="recents_empty_message" msgid="2269156590813544104">"JAUNÄ€KIE"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"InformÄcija par lietojumprogrammu"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"MeklÄ“t"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"MazÄk steidzami paziņojumi tiek rÄdÄ«ti tÄlÄk"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Pieskarieties vÄ“lreiz, lai atvÄ“rtu"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Velciet uz augÅ¡u, lai atbloÄ·Ä“tu"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"LÄ«dz brÄ«dim, kad izslÄ“gsiet"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Vienu minÅ«ti"</item> + <item quantity="other" msgid="6924190729213550991">"%d min"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Vienu stundu"</item> + <item quantity="other" msgid="5408537517529822157">"%d h"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml index 773a6fb..c00b7ed 100644 --- a/packages/SystemUI/res/values-mn-rMN/strings.xml +++ b/packages/SystemUI/res/values-mn-rMN/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Блютүүт <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Байршил <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"СÑрүүлгийг <xliff:g id="TIME">%s</xliff:g>-д тохируулÑан."</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G дата идÑвхгүй болов"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G дата идÑвхгүй байна"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Мобайл дата идÑвхгүй болов"</string> @@ -207,6 +213,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Ó¨Ó©Ñ€ тохиргоо"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Модем болгох"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"СүлжÑÑний цÑг"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"СҮҮЛИЙÐ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ðппликешны мÑдÑÑлÑл"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"хайх"</string> @@ -226,4 +234,10 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Яаралтай биш мÑдÑгдлүүдийг доор"</string> <string name="notification_tap_again" msgid="7590196980943943842">"ÐÑÑхийн тулд дахин товшино уу"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"ТүгжÑÑг тайлах бол шудрана уу"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index 09e52cc..263fc46 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokasi <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Penggera ditetapkan pada <xliff:g id="TIME">%s</xliff:g>."</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data 2G-3G dilumpuhkan"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data 4G dilumpuhkan"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Data mudah alih dilumpuhkan"</string> @@ -207,6 +213,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Lagi tetapan"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Penambatan"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Tempat liputan"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"TERBAHARU"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maklumat Aplikasi"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string> @@ -223,8 +231,13 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"%d lagi"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"Pemberitahuan kurang penting di bawah"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Ketik lagi untuk membuka"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Leret ke atas untuk membuka kunci"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 7ffd381..75706a2 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth – <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Posisjon <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmen ble stilt for <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Lukk panelet"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mer tid"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mindre tid"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G-data er deaktivert"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-data er deaktivert"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobildata er deaktivert"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Flere innstillinger"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tilknytning"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Wi-Fi-sone"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Varsler"</string> <string name="recents_empty_message" msgid="2269156590813544104">"NYLIGE"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformasjon"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre presserende varsler nedenfor"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Trykk pÃ¥ nytt for Ã¥ Ã¥pne"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Sveip oppover for Ã¥ lÃ¥se opp"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Inntil du slÃ¥r av funksjonen"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"I ett minutt"</item> + <item quantity="other" msgid="6924190729213550991">"I %d minutter"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"I én time"</item> + <item quantity="other" msgid="5408537517529822157">"I %d timer"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index e79adf9..c4bef68 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Locatie <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm is ingesteld op <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Deelvenster sluiten"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Meer tijd"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Minder tijd"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-/3G-gegevens uitgeschakeld"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G-gegevens uitgeschakeld"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobiele gegevens uitgeschakeld"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Meer instellingen"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Meldingen"</string> <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"App-informatie"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Minder urgente meldingen onderaan"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tik nogmaals om te openen"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Veeg omhoog om te ontgrendelen"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Totdat u dit uitschakelt"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Eén minuut"</item> + <item quantity="other" msgid="6924190729213550991">"%d minuten"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Eén uur"</item> + <item quantity="other" msgid="5408537517529822157">"%d uur"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index aec7e87..825dfec 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokalizacja <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm ustawiony na <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zamknij panel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"WiÄ™cej czasu"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mniej czasu"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Wyłączono transmisjÄ™ danych 2G/3G"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Wyłączono transmisjÄ™ danych 4G"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Transmisja danych zostaÅ‚a wyłączona"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"WiÄ™cej ustawieÅ„"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"PowiÄ…zanie"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Punkt dostÄ™pu"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Powiadomienia"</string> <string name="recents_empty_message" msgid="2269156590813544104">"OSTATNIE"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacje o aplikacji"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Poniżej widać mniej pilne powiadomienia"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Kliknij ponownie, by otworzyć"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"PrzesuÅ„ w górÄ™, by odblokować"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Dopóki nie wyłączysz"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Przez minutÄ™"</item> + <item quantity="other" msgid="6924190729213550991">"Przez %d min"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Przez godzinÄ™"</item> + <item quantity="other" msgid="5408537517529822157">"Przez %d godz."</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 93c7f60..f9ac828 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localização <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fechar painel"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mais tempo"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tempo"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Os dados 2G-3G estão desativados"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Os dados 4G estão desativados"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Os dados móveis estão desativados"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Mais definições"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Associação"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string> <string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações da aplicação"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificações menos urgentes abaixo"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar rapidamente com o dedo para cima para desbloquear"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Até que o utilizador desative"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Durante um minuto"</item> + <item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Durante uma hora"</item> + <item quantity="other" msgid="5408537517529822157">"Durante %d horas"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 079c8f0..1c13e62 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -158,6 +158,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localização <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dados 2G e 3G desativados"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dados 4G desativados"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dados móveis desativados"</string> @@ -204,14 +210,12 @@ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Transmitir tela"</string> <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string> - <!-- no translation found for quick_settings_inversion_label (8790919884718619648) --> - <skip /> + <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string> <string name="quick_settings_color_space_label" msgid="853443689745584770">"Modo de correção de cor"</string> - <!-- no translation found for quick_settings_more_settings (326112621462813682) --> - <skip /> - <!-- no translation found for quick_settings_tethering_label (7153452060448575549) --> - <skip /> - <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) --> + <string name="quick_settings_more_settings" msgid="326112621462813682">"Mais configurações"</string> + <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string> + <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Ponto de acesso"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do aplicativo"</string> @@ -229,8 +233,13 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"Mais %d"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificações menos urgentes abaixo"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Deslize para cima para desbloquear"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml index b9d121a..00bf8c0 100644 --- a/packages/SystemUI/res/values-rm/strings.xml +++ b/packages/SystemUI/res/values-rm/strings.xml @@ -282,6 +282,12 @@ <skip /> <!-- no translation found for accessibility_quick_settings_alarm (3959908972897295660) --> <skip /> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <!-- no translation found for data_usage_disabled_dialog_3g_title (5257833881698644687) --> <skip /> <!-- no translation found for data_usage_disabled_dialog_4g_title (4789143363492682629) --> @@ -384,6 +390,8 @@ <skip /> <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) --> <skip /> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <!-- no translation found for recents_empty_message (2269156590813544104) --> <skip /> <!-- no translation found for recents_app_info_button_label (2890317189376000030) --> @@ -411,4 +419,10 @@ <skip /> <!-- no translation found for keyguard_unlock (8043466894212841998) --> <skip /> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 10afe88..7ffa77c 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"LocaÈ›ie: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmă setată pentru <xliff:g id="TIME">%s</xliff:g>."</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datele 2G-3G au fost dezactivate"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datele 4G au fost dezactivate"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Datele mobile au fost dezactivate"</string> @@ -207,6 +213,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Mai multe setări"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"InformaÈ›ii despre aplicaÈ›ie"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string> @@ -223,8 +231,13 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"ÃŽncă %d"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"Notificările mai puÈ›in urgente mai jos"</string> <string name="notification_tap_again" msgid="7590196980943943842">"AtingeÈ›i din nou pentru a deschide"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"GlisaÈ›i în sus pentru a debloca"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index c726014..d726428 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ДоÑтуп к геоданным <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Будильник уÑтановлен на <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Закрыть панель."</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Увеличить продолжительноÑть."</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Уменьшить продолжительноÑть."</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Передача данных по каналам 2G и 3G отключена"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Передача данных по каналу 4G отключена"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Моб. Интернет отключен"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Дополнительные наÑтройки"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Режим модема"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка доÑтупа"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"УведомлениÑ"</string> <string name="recents_empty_message" msgid="2269156590813544104">"ÐЕДÐÐ’ÐИЕ СООБЩЕÐИЯ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ приложении"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"поиÑк"</string> @@ -228,4 +232,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Показать менее важные оповещениÑ"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Ðажмите ещё раз, чтобы открыть"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸ проведите пальцем по Ñкрану"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Пока Ñ Ð½Ðµ отключу"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"1 мин."</item> + <item quantity="other" msgid="6924190729213550991">"%d мин."</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"1 ч."</item> + <item quantity="other" msgid="5408537517529822157">"%d ч."</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 45bb2ad..8debc9a 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -158,6 +158,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Poloha: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"BudÃk nastavený na <xliff:g id="TIME">%s</xliff:g>."</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dátové prenosy 2G a 3G sú zakázané"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dátové prenosy 4G sú zakázané"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilné dátové prenosy sú zakázané"</string> @@ -209,6 +215,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"ÄŽalÅ¡ie nastavenia"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Zdieľanie dátového pripojenia"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"NEDÃVNE"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informácie o aplikácii"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"hľadaÅ¥"</string> @@ -228,4 +236,10 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Menej naliehavé upozornenia sa nachádzajú nižšie"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Upozornenie otvorÃte opätovným klepnutÃm"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Zariadenie odomknete prejdenÃm prstom nahor"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 99470d6..6171c16 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokacija: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm je nastavljen na <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zapri podokno"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"DaljÅ¡i Äas"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"KrajÅ¡i Äas"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Podatki 2G-3G so onemogoÄeni"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Podatki 4G so onemogoÄeni"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilni podatki so onemogoÄeni"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"VeÄ nastavitev"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Internet prek mobilne naprave"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Dostopna toÄka"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obvestila"</string> <string name="recents_empty_message" msgid="2269156590813544104">"NEDAVNI"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Podatki o aplikaciji"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Manj nujna obvestila spodaj"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Znova se dotaknite, da odprete"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Povlecite, da odklenete"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Dokler tega ne izklopite"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Za eno minuto"</item> + <item quantity="other" msgid="6924190729213550991">"Za %d min"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Za eno uro"</item> + <item quantity="other" msgid="5408537517529822157">"Za %d h"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 1e40ed6..14782e6 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Локација је <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Ðларм је подешен за <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Затворите таблу"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Више времена"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Мање времена"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G–3G подаци Ñу онемогућени"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G подаци Ñу онемогућени"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Подаци мобилне мреже Ñу онемогућени"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Још подешавања"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Повезивање"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ХотÑпот"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Обавештења"</string> <string name="recents_empty_message" msgid="2269156590813544104">"ÐÐЈÐОВИЈЕ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Информације о апликацији"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"претражи"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Мање хитна обавештења Ñу у наÑтавку"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Додирните поново да биÑте отворили"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Превуците нагоре да биÑте откључали"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Док не иÑкључите"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Један минут"</item> + <item quantity="other" msgid="6924190729213550991">"%d мин"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Један Ñат"</item> + <item quantity="other" msgid="5408537517529822157">"%d Ñ"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index e4d7471..a7fb15d 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Plats <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmet ringer <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Stäng panelen"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Längre tid"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kortare tid"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data via 2G-3G har inaktiverats"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data via 4G har inaktiverats"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobildata har inaktiverats"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Fler inställningar"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Internetdelning"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"TrÃ¥dlös surfzon"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Aviseringar"</string> <string name="recents_empty_message" msgid="2269156590813544104">"NYA"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformation"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre brÃ¥dskande aviseringar nedan"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tryck igen för att öppna"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Dra uppÃ¥t om du vill lÃ¥sa upp"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Tills du inaktiverar detta"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"I en minut"</item> + <item quantity="other" msgid="6924190729213550991">"I %d minuter"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"I en timme"</item> + <item quantity="other" msgid="5408537517529822157">"I %d timmar"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1d27854..b8d8c58 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -154,6 +154,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Mahali <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Kengele imewekwa <xliff:g id="TIME">%s</xliff:g>."</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data ya 2G-3G imelemazwa"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data ya 4G imelemazwa"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Data ya kifaa cha mkononi imelemazwa"</string> @@ -205,6 +211,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Mipangilio zaidi"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Kusambaza mtandao"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Mtandao-hewa"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"YA HIVI KARIBUNI"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Maelezo ya Programu"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"tafuta"</string> @@ -221,7 +229,13 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"%d zaidi"</item> </plurals> - <string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizokuwa za dharura sana hapo chini"</string> + <string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizo za dharura sana ziko hapo chini"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Gonga tena ili ufungue"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Telezesha kidole ili ufungue"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 306416b..dc75e78 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"บลูทูธ <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"สถานที่ <xliff:g id="STATE">%s</xliff:g>"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ตั้งเวลาปลุà¸à¹„ว้ที่ <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"ปิดใช้งานข้à¸à¸¡à¸¹à¸¥ 2G-3G à¹à¸¥à¹‰à¸§"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"ปิดใช้งานข้à¸à¸¡à¸¹à¸¥ 4G à¹à¸¥à¹‰à¸§"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"ปิดใช้งานข้à¸à¸¡à¸¹à¸¥à¸¡à¸·à¸à¸–ืà¸à¹à¸¥à¹‰à¸§"</string> @@ -207,6 +213,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"à¸à¸²à¸£à¸•ั้งค่าเพิ่มเติม"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"à¸à¸²à¸£à¸›à¸¥à¹ˆà¸à¸¢à¸ªà¸±à¸à¸à¸²à¸“"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ฮà¸à¸•สปà¸à¸•"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"ล่าสุด"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"ข้à¸à¸¡à¸¹à¸¥à¹à¸à¸›à¸žà¸¥à¸´à¹€à¸„ชัน"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"ค้นหา"</string> @@ -223,7 +231,13 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"à¸à¸µà¸ %d"</item> </plurals> - <string name="speed_bump_explanation" msgid="1288875699658819755">"à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸—ี่เร่งด่วนน้à¸à¸¢à¸—างด้านล่าง"</string> + <string name="speed_bump_explanation" msgid="1288875699658819755">"à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸—ี่เร่งด่วนน้à¸à¸¢à¸”้านล่าง"</string> <string name="notification_tap_again" msgid="7590196980943943842">"à¹à¸•ะà¸à¸µà¸à¸„รั้งเพื่à¸à¹€à¸›à¸´à¸”"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"à¸à¸§à¸²à¸”ขึ้นเพื่à¸à¸›à¸¥à¸”ล็à¸à¸"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index debef15..0713ddd 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"<xliff:g id="STATE">%s</xliff:g> ng Bluetooth."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokasyon <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm set para sa <xliff:g id="TIME">%s</xliff:g>."</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Di pinapagana ang 2G-3G na data"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Hindi pinapagana ang 4G na data"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Hindi pinapagana ang data ng mobile"</string> @@ -202,14 +208,12 @@ <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"I-cast ang Screen"</string> <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string> - <!-- no translation found for quick_settings_inversion_label (8790919884718619648) --> - <skip /> + <string name="quick_settings_inversion_label" msgid="8790919884718619648">"I-invert ang mga kulay"</string> <string name="quick_settings_color_space_label" msgid="853443689745584770">"Mode ng pagtatama ng kulay"</string> - <!-- no translation found for quick_settings_more_settings (326112621462813682) --> - <skip /> - <!-- no translation found for quick_settings_tethering_label (7153452060448575549) --> - <skip /> - <!-- no translation found for quick_settings_hotspot_label (6046917934974004879) --> + <string name="quick_settings_more_settings" msgid="326112621462813682">"Marami pang setting"</string> + <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Nagte-tether"</string> + <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"MGA KAMAKAILAN"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string> @@ -227,8 +231,13 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"%d pa"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"Nasa ibaba ang mga notification na hindi masyadong mahalaga"</string> <string name="notification_tap_again" msgid="7590196980943943842">"I-tap ulit upang buksan"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Mag-swipe pataas upang i-unlock"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 506d27e..c0ba250 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -156,6 +156,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Konum: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm saati: <xliff:g id="TIME">%s</xliff:g>."</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G verileri devre dışı bırakıldı"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G verileri devre dışı"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobil veriler devre dışı"</string> @@ -207,6 +213,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"DiÄŸer ayarlar"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"SON İLETİLER"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Uygulama Bilgileri"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string> @@ -226,4 +234,10 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Daha az acil bildirimler aÅŸağıdadır"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Açmak için tekrar hafifçe vurun"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Kilidi açmak için hızlıca yukarı kaydırın"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 5b3c5b2..e149d7b 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"МіÑÑ†ÐµÐ·Ð½Ð°Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Сигнал уÑтановлено на <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Закрити панель"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Більше чаÑу"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Менше чаÑу"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Дані 2G–3G вимкнено"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Дані 4G вимкнено"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Мобільне Ð¿ÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… вимкнено"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Більше налаштувань"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Режим модема"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка доÑтупу"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"СповіщеннÑ"</string> <string name="recents_empty_message" msgid="2269156590813544104">"ОСТÐÐÐІ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ додаток"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Менше термінових Ñповіщень нижче"</string> <string name="notification_tap_again" msgid="7590196980943943842">"ТоркнітьÑÑ Ð·Ð½Ð¾Ð²Ñƒ, щоб відкрити"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Проведіть пальцем угору, щоб розблокувати"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Доки ви не вимкнете"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"ПротÑгом хвилини"</item> + <item quantity="other" msgid="6924190729213550991">"ПротÑгом %d хв"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"ПротÑгом години"</item> + <item quantity="other" msgid="5408537517529822157">"ПротÑгом %d год"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index c6fddfe..f99c341 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Vị trà <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Báo thức được đặt cho <xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Äóng bảng Ä‘iá»u khiển"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Nhiá»u thá»i gian hÆ¡n"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Ãt thá»i gian hÆ¡n"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Äã tắt dữ liệu 2G-3G"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Äã tắt dữ liệu 4G"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dữ liệu di động bị vô hiệu hóa"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Cà i đặt khác"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Äang dùng là m Ä‘iểm truy cáºp Internet"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Äiểm phát sóng"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Thông báo"</string> <string name="recents_empty_message" msgid="2269156590813544104">"GẦN ÄÂY"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Thông tin ứng dụng"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Thông báo Ãt khẩn cấp hÆ¡n bên dưới"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Nhấn lại để mở"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Vuốt lên để mở khóa"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Cho đến khi bạn tắt tÃnh năng nà y"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Trong má»™t phút"</item> + <item quantity="other" msgid="6924190729213550991">"Trong %d phút"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Trong má»™t giá»"</item> + <item quantity="other" msgid="5408537517529822157">"Trong %d giá»"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index c6da6ac..6940b2c 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -158,6 +158,12 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"è“牙:<xliff:g id="STATE">%s</xliff:g>。"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"ä½ç½®ä¿¡æ¯æœåŠ¡<xliff:g id="STATE">%s</xliff:g>。"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"闹钟已设置为:<xliff:g id="TIME">%s</xliff:g>。"</string> + <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) --> + <skip /> + <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) --> + <skip /> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G æ•°æ®ç½‘络已åœç”¨"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G æ•°æ®ç½‘络已åœç”¨"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"移动数æ®ç½‘络已åœç”¨"</string> @@ -209,6 +215,8 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"更多设置"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"网络共享"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"çƒç‚¹"</string> + <!-- no translation found for quick_settings_notifications_label (4818156442169154523) --> + <skip /> <string name="recents_empty_message" msgid="2269156590813544104">"最近"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"应用信æ¯"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"æœç´¢"</string> @@ -225,8 +233,13 @@ <plurals name="keyguard_more_overflow_text"> <item quantity="other" msgid="9180696159506883684">"还有%dæ¡"</item> </plurals> - <!-- no translation found for speed_bump_explanation (1288875699658819755) --> - <skip /> + <string name="speed_bump_explanation" msgid="1288875699658819755">"ä¸å¤ªç´§æ€¥çš„通知会显示在下方"</string> <string name="notification_tap_again" msgid="7590196980943943842">"冿¬¡ç‚¹æŒ‰å³å¯æ‰“å¼€"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"å‘上滑动å³å¯è§£é”"</string> + <!-- no translation found for zen_mode_forever (7420011936770086993) --> + <skip /> + <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) --> + <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) --> + <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) --> + <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) --> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index ce9e260..04c00f5 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"è—牙:<xliff:g id="STATE">%s</xliff:g>。"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"å®šä½æœå‹™<xliff:g id="STATE">%s</xliff:g>。"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"鬧é˜å·²è¨å®šç‚ºï¼š<xliff:g id="TIME">%s</xliff:g>。"</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"é—œé–‰é¢æ¿"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更多時間"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"更少時間"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"å·²åœç”¨ 2G-3G 數據"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"å·²åœç”¨ 4G 數據"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"å·²åœç”¨æµå‹•數據"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"更多è¨å®š"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"網路共用"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"熱點"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string> <string name="recents_empty_message" msgid="2269156590813544104">"近期"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程å¼è³‡æ–™"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"æœå°‹"</string> @@ -228,4 +232,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"ä¸å¤ªç·Šæ€¥çš„通知會在下方顯示"</string> <string name="notification_tap_again" msgid="7590196980943943842">"冿¬¡è¼•按å³å¯é–‹å•Ÿ"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"å‘上快速滑動å³å¯è§£éŽ–"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"æ‰‹å‹•é—œé–‰é€™é …è¨å®šå‰ä¸€å¾‹å•Ÿç”¨"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"1 分é˜"</item> + <item quantity="other" msgid="6924190729213550991">"%d 分é˜"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"1 å°æ™‚"</item> + <item quantity="other" msgid="5408537517529822157">"%d å°æ™‚"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index cafb688..4ffb01b 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -158,6 +158,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"è—牙:<xliff:g id="STATE">%s</xliff:g>。"</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"å®šä½æœå‹™<xliff:g id="STATE">%s</xliff:g>。"</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"鬧é˜å·²è¨å®šç‚ºï¼š<xliff:g id="TIME">%s</xliff:g>。"</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"é—œé–‰é¢æ¿"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更多時間"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"更少時間"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"å·²åœç”¨ 2G-3G 數據"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"å·²åœç”¨ 4G 數據"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"å·²åœç”¨è¡Œå‹•數據"</string> @@ -209,6 +212,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"更多è¨å®š"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"網路共用"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"無線基地å°"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string> <string name="recents_empty_message" msgid="2269156590813544104">"近期"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程å¼è³‡è¨Š"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"æœå°‹"</string> @@ -228,4 +232,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"較ä¸ç·Šæ€¥çš„通知會顯示在下方"</string> <string name="notification_tap_again" msgid="7590196980943943842">"冿¬¡è¼•按å³å¯é–‹å•Ÿ"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"å‘上滑動å³å¯è§£éŽ–"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"æ‰‹å‹•é—œé–‰é€™é …è¨å®šå‰ä¸€å¾‹å•Ÿç”¨"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"1 分é˜"</item> + <item quantity="other" msgid="6924190729213550991">"%d 分é˜"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"1 å°æ™‚"</item> + <item quantity="other" msgid="5408537517529822157">"%d å°æ™‚"</item> + </plurals> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 4865f7b..4c44972 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -156,6 +156,9 @@ <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"I-Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Indawo i-<xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"I-alamu isethiwe ngo-<xliff:g id="TIME">%s</xliff:g>."</string> + <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Vala iphaneli"</string> + <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Isikhathi esiningi"</string> + <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Isikhathi esingaphansi"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"idatha ye-2G-3G ivimbelwe"</string> <string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Idatha ye-4G ivimbelwe"</string> <string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Idatha yefoni ivimbelwe"</string> @@ -207,6 +210,7 @@ <string name="quick_settings_more_settings" msgid="326112621462813682">"Izilungiselelo eziningi"</string> <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Ukusebenzisa njengemodemu"</string> <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"I-Hotspot"</string> + <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Izaziso"</string> <string name="recents_empty_message" msgid="2269156590813544104">"OKWAKAMUVA"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Ulwazi lohlelo lokusebenza"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"sesha"</string> @@ -226,4 +230,13 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Izaziso ezingasheshi kakhulu ezingezansi"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Thepha futhi ukuze uvule"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"Swayiphela phezulu ukuze uvule"</string> + <string name="zen_mode_forever" msgid="7420011936770086993">"Uze uvale lokhu"</string> + <plurals name="zen_mode_duration_minutes"> + <item quantity="one" msgid="9040808414992812341">"Iminithi elilodwa"</item> + <item quantity="other" msgid="6924190729213550991">"Amaminithi angu-%d"</item> + </plurals> + <plurals name="zen_mode_duration_hours"> + <item quantity="one" msgid="3480040795582254384">"Ihora elilodwa"</item> + <item quantity="other" msgid="5408537517529822157">"Amahora angu-%d"</item> + </plurals> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index f7b4994..4837a53 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -59,8 +59,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.MultiUserAvatarCache; import com.android.keyguard.ViewMediatorCallback; -import com.android.keyguard.analytics.KeyguardAnalytics; -import com.android.keyguard.analytics.Session; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.ScrimController; @@ -70,7 +68,6 @@ import com.android.systemui.statusbar.phone.StatusBarWindowManager; import java.io.File; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; -import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapter; /** @@ -117,7 +114,6 @@ import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapte public class KeyguardViewMediator extends SystemUI { private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000; final static boolean DEBUG = false; - private static final boolean ENABLE_ANALYTICS = Build.IS_DEBUGGABLE; private final static boolean DBG_WAKE = false; private final static String TAG = "KeyguardViewMediator"; @@ -199,8 +195,6 @@ public class KeyguardViewMediator extends SystemUI { private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - private KeyguardAnalytics mKeyguardAnalytics; - // these are protected by synchronized (this) /** @@ -469,22 +463,6 @@ public class KeyguardViewMediator extends SystemUI { mViewMediatorCallback, mLockPatternUtils); final ContentResolver cr = mContext.getContentResolver(); - if (ENABLE_ANALYTICS && !LockPatternUtils.isSafeModeEnabled() && - Settings.Secure.getInt(cr, KEYGUARD_ANALYTICS_SETTING, 0) == 1) { - mKeyguardAnalytics = new KeyguardAnalytics(mContext, new SessionTypeAdapter() { - - @Override - public int getSessionType() { - return mLockPatternUtils.isSecure() && !mUpdateMonitor.getUserHasTrust( - mLockPatternUtils.getCurrentUser()) - ? Session.TYPE_KEYGUARD_SECURE - : Session.TYPE_KEYGUARD_INSECURE; - } - }, new File(mContext.getCacheDir(), "keyguard_analytics.bin")); - } else { - mKeyguardAnalytics = null; - } - mScreenOn = mPM.isScreenOn(); mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0); @@ -585,9 +563,6 @@ public class KeyguardViewMediator extends SystemUI { } else { doKeyguardLocked(null); } - if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) { - mKeyguardAnalytics.getCallback().onScreenOff(); - } } KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why); } @@ -830,9 +805,6 @@ public class KeyguardViewMediator extends SystemUI { updateActivityLockScreenState(); adjustStatusBarLocked(); } - if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) { - mKeyguardAnalytics.getCallback().onSetOccluded(isOccluded); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java index cadb44a..f978833 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.text.format.DateFormat; import android.util.AttributeSet; import android.widget.TextView; @@ -29,8 +30,6 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; -import libcore.icu.ICU; - public class DateView extends TextView { private static final String TAG = "DateView"; @@ -87,7 +86,7 @@ public class DateView extends TextView { if (mDateFormat == null) { final String dateFormat = getContext().getString(R.string.system_ui_date_pattern); final Locale l = Locale.getDefault(); - final String fmt = ICU.getBestDateTimePattern(dateFormat, l.toString()); + final String fmt = DateFormat.getBestDateTimePattern(l, dateFormat); mDateFormat = new SimpleDateFormat(fmt, l); } diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk index 79f8a1f..3c4e951 100644 --- a/packages/services/PacProcessor/Android.mk +++ b/packages/services/PacProcessor/Android.mk @@ -27,8 +27,6 @@ LOCAL_CERTIFICATE := platform LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor -LOCAL_MULTILIB := 32 - include $(BUILD_PACKAGE) include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/services/PacProcessor/jni/Android.mk b/packages/services/PacProcessor/jni/Android.mk index 8a60927..f16c90b 100644 --- a/packages/services/PacProcessor/jni/Android.mk +++ b/packages/services/PacProcessor/jni/Android.mk @@ -35,7 +35,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE := libjni_pacprocessor LOCAL_MODULE_TAGS := optional -LOCAL_32_BIT_ONLY := true include external/stlport/libstlport.mk diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 30282dd..0d39586 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -289,6 +289,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mDemoHdmiRotation; boolean mDemoHdmiRotationLock; + boolean mWakeGestureEnabledSetting; + MyWakeGestureListener mWakeGestureListener; + // Default display does not rotate, apps that require non-default orientation will have to // have the orientation emulated. private boolean mForceDefaultOrientation = false; @@ -525,6 +528,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.WAKE_GESTURE_ENABLED), false, this, + UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.ACCELEROMETER_ROTATION), false, this, UserHandle.USER_ALL); @@ -555,6 +561,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + class MyWakeGestureListener extends WakeGestureListener { + MyWakeGestureListener(Context context, Handler handler) { + super(context, handler); + } + + @Override + public void onWakeUp() { + synchronized (mLock) { + if (shouldEnableWakeGestureLp()) { + mPowerManager.wakeUp(SystemClock.uptimeMillis()); + } + } + } + } + class MyOrientationListener extends WindowOrientationListener { MyOrientationListener(Context context, Handler handler) { super(context, handler); @@ -856,6 +877,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManager = windowManager; mWindowManagerFuncs = windowManagerFuncs; mHandler = new PolicyHandler(); + mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler); mOrientationListener = new MyOrientationListener(mContext, mHandler); try { mOrientationListener.setCurrentRotation(windowManager.getRotation()); @@ -1142,6 +1164,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT, UserHandle.USER_CURRENT); + // Configure wake gesture. + boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver, + Settings.Secure.WAKE_GESTURE_ENABLED, 0, + UserHandle.USER_CURRENT) != 0; + if (mWakeGestureEnabledSetting != wakeGestureEnabledSetting) { + mWakeGestureEnabledSetting = wakeGestureEnabledSetting; + updateWakeGestureListenerLp(); + } + // Configure rotation lock. int userRotation = Settings.System.getIntForUser(resolver, Settings.System.USER_ROTATION, Surface.ROTATION_0, @@ -1189,6 +1220,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void updateWakeGestureListenerLp() { + if (shouldEnableWakeGestureLp()) { + mWakeGestureListener.requestWakeUpTrigger(); + } else { + mWakeGestureListener.cancelWakeUpTrigger(); + } + } + + private boolean shouldEnableWakeGestureLp() { + return mWakeGestureEnabledSetting && !mScreenOnEarly + && (!mLidControlsSleep || mLidState != LID_CLOSED) + && mWakeGestureListener.isSupported(); + } + private void enablePointerLocation() { if (mPointerLocationView == null) { mPointerLocationView = new PointerLocationView(mContext); @@ -4410,6 +4455,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDelegate.onScreenTurnedOff(why); } synchronized (mLock) { + updateWakeGestureListenerLp(); updateOrientationListenerLp(); updateLockScreenTimeout(); } @@ -4426,6 +4472,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { synchronized (mLock) { mScreenOnEarly = true; + updateWakeGestureListenerLp(); updateOrientationListenerLp(); updateLockScreenTimeout(); } @@ -5021,6 +5068,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { PowerManager.GO_TO_SLEEP_REASON_USER, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); } + + synchronized (mLock) { + updateWakeGestureListenerLp(); + } } void updateRotation(boolean alwaysSendConfiguration) { @@ -5481,6 +5532,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mLastFocusNeedsMenu="); pw.println(mLastFocusNeedsMenu); } + pw.print(prefix); pw.print("mWakeGestureEnabledSetting="); + pw.println(mWakeGestureEnabledSetting); + pw.print(prefix); pw.print("mSupportAutoRotation="); pw.println(mSupportAutoRotation); pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode); pw.print(" mDockMode="); pw.print(mDockMode); @@ -5622,9 +5676,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation); pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock); pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation); + mGlobalKeyManager.dump(prefix, pw); mStatusBarController.dump(pw, prefix); mNavigationBarController.dump(pw, prefix); PolicyControl.dump(prefix, pw); + + if (mWakeGestureListener != null) { + mWakeGestureListener.dump(pw, prefix); + } + if (mOrientationListener != null) { + mOrientationListener.dump(pw, prefix); + } } } diff --git a/policy/src/com/android/internal/policy/impl/WakeGestureListener.java b/policy/src/com/android/internal/policy/impl/WakeGestureListener.java new file mode 100644 index 0000000..9396c2c --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/WakeGestureListener.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.policy.impl; + +import android.os.Handler; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.hardware.TriggerEvent; +import android.hardware.TriggerEventListener; + +import java.io.PrintWriter; + +/** + * Watches for wake gesture sensor events then invokes the listener. + */ +public abstract class WakeGestureListener { + private static final String TAG = "WakeGestureListener"; + + private final SensorManager mSensorManager; + private final Handler mHandler; + + private final Object mLock = new Object(); + + private boolean mTriggerRequested; + private Sensor mSensor; + + public WakeGestureListener(Context context, Handler handler) { + mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); + mHandler = handler; + + mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_WAKE_GESTURE); + } + + public abstract void onWakeUp(); + + public boolean isSupported() { + synchronized (mLock) { + return mSensor != null; + } + } + + public void requestWakeUpTrigger() { + synchronized (mLock) { + if (mSensor != null && !mTriggerRequested) { + mTriggerRequested = true; + mSensorManager.requestTriggerSensor(mListener, mSensor); + } + } + } + + public void cancelWakeUpTrigger() { + synchronized (mLock) { + if (mSensor != null && mTriggerRequested) { + mTriggerRequested = false; + mSensorManager.cancelTriggerSensor(mListener, mSensor); + } + } + } + + public void dump(PrintWriter pw, String prefix) { + synchronized (mLock) { + pw.println(prefix + TAG); + prefix += " "; + pw.println(prefix + "mTriggerRequested=" + mTriggerRequested); + pw.println(prefix + "mSensor=" + mSensor); + } + } + + private final TriggerEventListener mListener = new TriggerEventListener() { + @Override + public void onTrigger(TriggerEvent event) { + synchronized (mLock) { + mTriggerRequested = false; + mHandler.post(mWakeUpRunnable); + } + } + }; + + private final Runnable mWakeUpRunnable = new Runnable() { + @Override + public void run() { + onWakeUp(); + } + }; +} diff --git a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java index 0c77556..2cc33b5 100644 --- a/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java +++ b/policy/src/com/android/internal/policy/impl/WindowOrientationListener.java @@ -26,6 +26,9 @@ import android.os.SystemProperties; import android.util.FloatMath; import android.util.Log; import android.util.Slog; +import android.util.TimeUtils; + +import java.io.PrintWriter; /** * A special helper class used by the WindowManager @@ -181,6 +184,19 @@ public abstract class WindowOrientationListener { */ public abstract void onProposedRotationChanged(int rotation); + public void dump(PrintWriter pw, String prefix) { + synchronized (mLock) { + pw.println(prefix + TAG); + prefix += " "; + pw.println(prefix + "mEnabled=" + mEnabled); + pw.println(prefix + "mCurrentRotation=" + mCurrentRotation); + pw.println(prefix + "mSensor=" + mSensor); + pw.println(prefix + "mRate=" + mRate); + + mSensorEventListener.dumpLocked(pw, prefix); + } + } + /** * This class filters the raw accelerometer data and tries to detect actual changes in * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters, @@ -342,6 +358,14 @@ public abstract class WindowOrientationListener { /* ROTATION_270 */ { -25, 65 } }; + // The tilt angle below which we conclude that the user is holding the device + // overhead reading in bed and lock into that state. + private final int TILT_OVERHEAD_ENTER = -40; + + // The tilt angle above which we conclude that the user would like a rotation + // change to occur and unlock from the overhead state. + private final int TILT_OVERHEAD_EXIT = -15; + // The gap angle in degrees between adjacent orientation angles for hysteresis. // This creates a "dead zone" between the current orientation and a proposed // adjacent orientation. No orientation proposal is made when the orientation @@ -364,12 +388,18 @@ public abstract class WindowOrientationListener { // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed). private long mFlatTimestampNanos; + private boolean mFlat; // Timestamp when the device last appeared to be swinging. private long mSwingTimestampNanos; + private boolean mSwinging; // Timestamp when the device last appeared to be undergoing external acceleration. private long mAccelerationTimestampNanos; + private boolean mAccelerating; + + // Whether we are locked into an overhead usage mode. + private boolean mOverhead; // History of observed tilt angles. private static final int TILT_HISTORY_SIZE = 40; @@ -381,6 +411,19 @@ public abstract class WindowOrientationListener { return mProposedRotation; } + public void dumpLocked(PrintWriter pw, String prefix) { + pw.println(prefix + "mProposedRotation=" + mProposedRotation); + pw.println(prefix + "mPredictedRotation=" + mPredictedRotation); + pw.println(prefix + "mLastFilteredX=" + mLastFilteredX); + pw.println(prefix + "mLastFilteredY=" + mLastFilteredY); + pw.println(prefix + "mLastFilteredZ=" + mLastFilteredZ); + pw.println(prefix + "mTiltHistory={last: " + getLastTiltLocked() + "}"); + pw.println(prefix + "mFlat=" + mFlat); + pw.println(prefix + "mSwinging=" + mSwinging); + pw.println(prefix + "mAccelerating=" + mAccelerating); + pw.println(prefix + "mOverhead=" + mOverhead); + } + @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @@ -478,7 +521,18 @@ public abstract class WindowOrientationListener { // If the tilt angle is too close to horizontal then we cannot determine // the orientation angle of the screen. - if (Math.abs(tiltAngle) > MAX_TILT) { + if (tiltAngle <= TILT_OVERHEAD_ENTER) { + mOverhead = true; + } else if (tiltAngle >= TILT_OVERHEAD_EXIT) { + mOverhead = false; + } + if (mOverhead) { + if (LOG) { + Slog.v(TAG, "Ignoring sensor data, device is overhead: " + + "tiltAngle=" + tiltAngle); + } + clearPredictedRotationLocked(); + } else if (Math.abs(tiltAngle) > MAX_TILT) { if (LOG) { Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " + "tiltAngle=" + tiltAngle); @@ -526,6 +580,9 @@ public abstract class WindowOrientationListener { } } } + mFlat = isFlat; + mSwinging = isSwinging; + mAccelerating = isAccelerating; // Determine new proposed rotation. oldProposedRotation = mProposedRotation; @@ -543,6 +600,7 @@ public abstract class WindowOrientationListener { + ", isAccelerating=" + isAccelerating + ", isFlat=" + isFlat + ", isSwinging=" + isSwinging + + ", isOverhead=" + mOverhead + ", timeUntilSettledMS=" + remainingMS(now, mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now, @@ -660,8 +718,12 @@ public abstract class WindowOrientationListener { mLastFilteredTimestampNanos = Long.MIN_VALUE; mProposedRotation = -1; mFlatTimestampNanos = Long.MIN_VALUE; + mFlat = false; mSwingTimestampNanos = Long.MIN_VALUE; + mSwinging = false; mAccelerationTimestampNanos = Long.MIN_VALUE; + mAccelerating = false; + mOverhead = false; clearPredictedRotationLocked(); clearTiltHistoryLocked(); } @@ -726,6 +788,11 @@ public abstract class WindowOrientationListener { return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1; } + private float getLastTiltLocked() { + int index = nextTiltHistoryIndexLocked(mTiltHistoryIndex); + return index >= 0 ? mTiltHistory[index] : Float.NaN; + } + private float remainingMS(long now, long until) { return now >= until ? 0 : (until - now) * 0.000001f; } diff --git a/services/Android.mk b/services/Android.mk index 5fcef64..b4de903 100644 --- a/services/Android.mk +++ b/services/Android.mk @@ -25,6 +25,7 @@ services := \ backup \ devicepolicy \ print \ + restrictions \ usb \ voiceinteraction diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 87b1d32..7a67d63 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -843,6 +843,15 @@ class AppWidgetServiceImpl { throw new IllegalArgumentException("Unknown component " + componentName); } + // Ensure that the service specified by the passed intent belongs to the same package + // as provides the passed widget id. + String widgetIdPackage = id.provider.info.provider.getPackageName(); + String servicePackage = componentName.getPackageName(); + if (!servicePackage.equals(widgetIdPackage)) { + throw new SecurityException("Specified intent doesn't belong to the same package" + + " as the provided AppWidget id"); + } + // If there is already a connection made for this service intent, then disconnect from // that first. (This does not allow multiple connections to the same service under // the same key) diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 0082b1e..14c15a7 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -26,6 +26,7 @@ import android.app.PendingIntent; import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.app.backup.BackupTransport; import android.app.backup.FullBackup; import android.app.backup.RestoreSet; import android.app.backup.IBackupManager; @@ -82,7 +83,6 @@ import android.util.Slog; import android.util.SparseArray; import android.util.StringBuilderPrinter; -import com.android.internal.backup.BackupConstants; import com.android.internal.backup.IBackupTransport; import com.android.internal.backup.IObbBackupService; import com.android.server.AppWidgetBackupBridge; @@ -2098,7 +2098,7 @@ public class BackupManagerService extends IBackupManager.Stub { } mAgentBinder = null; - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; // Sanity check: if the queue is empty we have no work to do. if (mOriginalQueue.isEmpty()) { @@ -2121,14 +2121,14 @@ public class BackupManagerService extends IBackupManager.Stub { EventLog.writeEvent(EventLogTags.BACKUP_START, transportName); // If we haven't stored package manager metadata yet, we must init the transport. - if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) { + if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) { Slog.i(TAG, "Initializing (wiping) backup state and transport storage"); addBackupTrace("initializing transport " + transportName); resetBackupState(mStateDir); // Just to make sure. mStatus = mTransport.initializeDevice(); addBackupTrace("transport.initializeDevice() == " + mStatus); - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); } else { EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); @@ -2141,7 +2141,7 @@ public class BackupManagerService extends IBackupManager.Stub { // directly and use a synthetic BackupRequest. We always run this pass // because it's cheap and this way we guarantee that we don't get out of // step even if we're selecting among various transports at run time. - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( mPackageManager, allAgentPackages()); mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL, @@ -2149,7 +2149,7 @@ public class BackupManagerService extends IBackupManager.Stub { addBackupTrace("PMBA invoke: " + mStatus); } - if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) { + if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { // The backend reports that our dataset has been wiped. Note this in // the event log; the no-success code below will reset the backup // state as well. @@ -2158,13 +2158,13 @@ public class BackupManagerService extends IBackupManager.Stub { } catch (Exception e) { Slog.e(TAG, "Error in backup thread", e); addBackupTrace("Exception in backup thread: " + e); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; } finally { // If we've succeeded so far, invokeAgentForBackup() will have run the PM // metadata and its completion/timeout callback will continue the state // machine chain. If it failed that won't happen; we handle that now. addBackupTrace("exiting prelim: " + mStatus); - if (mStatus != BackupConstants.TRANSPORT_OK) { + if (mStatus != BackupTransport.TRANSPORT_OK) { // if things went wrong at this point, we need to // restage everything and try again later. resetBackupState(mStateDir); // Just to make sure. @@ -2176,7 +2176,7 @@ public class BackupManagerService extends IBackupManager.Stub { // Transport has been initialized and the PM metadata submitted successfully // if that was warranted. Now we process the single next thing in the queue. void invokeNextAgent() { - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; addBackupTrace("invoke q=" + mQueue.size()); // Sanity check that we have work to do. If not, skip to the end where @@ -2236,39 +2236,39 @@ public class BackupManagerService extends IBackupManager.Stub { // done here as long as we're successful so far. } else { // Timeout waiting for the agent - mStatus = BackupConstants.AGENT_ERROR; + mStatus = BackupTransport.AGENT_ERROR; } } catch (SecurityException ex) { // Try for the next one. Slog.d(TAG, "error in bind/backup", ex); - mStatus = BackupConstants.AGENT_ERROR; + mStatus = BackupTransport.AGENT_ERROR; addBackupTrace("agent SE"); } } catch (NameNotFoundException e) { Slog.d(TAG, "Package does not exist; skipping"); addBackupTrace("no such package"); - mStatus = BackupConstants.AGENT_UNKNOWN; + mStatus = BackupTransport.AGENT_UNKNOWN; } finally { mWakelock.setWorkSource(null); // If there was an agent error, no timeout/completion handling will occur. // That means we need to direct to the next state ourselves. - if (mStatus != BackupConstants.TRANSPORT_OK) { + if (mStatus != BackupTransport.TRANSPORT_OK) { BackupState nextState = BackupState.RUNNING_QUEUE; mAgentBinder = null; // An agent-level failure means we reenqueue this one agent for // a later retry, but otherwise proceed normally. - if (mStatus == BackupConstants.AGENT_ERROR) { + if (mStatus == BackupTransport.AGENT_ERROR) { if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName + " - restaging"); dataChangedImpl(request.packageName); - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; if (mQueue.isEmpty()) nextState = BackupState.FINAL; - } else if (mStatus == BackupConstants.AGENT_UNKNOWN) { + } else if (mStatus == BackupTransport.AGENT_UNKNOWN) { // Failed lookup of the app, so we couldn't bring up an agent, but // we're otherwise fine. Just drop it and go on to the next as usual. - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; } else { // Transport-level failure means we reenqueue everything revertAndEndBackup(); @@ -2297,7 +2297,7 @@ public class BackupManagerService extends IBackupManager.Stub { // If everything actually went through and this is the first time we've // done a backup, we can now record what the current backup dataset token // is. - if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) { + if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) { addBackupTrace("success; recording token"); try { mCurrentToken = mTransport.getCurrentRestoreSet(); @@ -2314,7 +2314,7 @@ public class BackupManagerService extends IBackupManager.Stub { // state machine sequence and the wakelock is refcounted. synchronized (mQueueLock) { mBackupRunning = false; - if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) { + if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { // Make sure we back up everything and perform the one-time init clearMetadata(); if (DEBUG) Slog.d(TAG, "Server requires init; rerunning"); @@ -2395,7 +2395,7 @@ public class BackupManagerService extends IBackupManager.Stub { EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString()); agentErrorCleanup(); - return BackupConstants.AGENT_ERROR; + return BackupTransport.AGENT_ERROR; } // At this point the agent is off and running. The next thing to happen will @@ -2403,7 +2403,7 @@ public class BackupManagerService extends IBackupManager.Stub { // for transport, or a timeout. Either way the next phase will happen in // response to the TimeoutHandler interface callbacks. addBackupTrace("invoke success"); - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } public void failAgent(IBackupAgent agent, String message) { @@ -2484,11 +2484,11 @@ public class BackupManagerService extends IBackupManager.Stub { addBackupTrace("operation complete"); ParcelFileDescriptor backupData = null; - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; try { int size = (int) mBackupDataName.length(); if (size > 0) { - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { backupData = ParcelFileDescriptor.open(mBackupDataName, ParcelFileDescriptor.MODE_READ_ONLY); addBackupTrace("sending data to transport"); @@ -2501,7 +2501,7 @@ public class BackupManagerService extends IBackupManager.Stub { // renaming *all* the output state files (see below) until that happens. addBackupTrace("data delivered: " + mStatus); - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { addBackupTrace("finishing op on transport"); mStatus = mTransport.finishBackup(); addBackupTrace("finished: " + mStatus); @@ -2514,7 +2514,7 @@ public class BackupManagerService extends IBackupManager.Stub { // After successful transport, delete the now-stale data // and juggle the files so that next time we supply the agent // with the new state file it just created. - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { mBackupDataName.delete(); mNewStateName.renameTo(mSavedStateName); EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size); @@ -2525,7 +2525,7 @@ public class BackupManagerService extends IBackupManager.Stub { } catch (Exception e) { Slog.e(TAG, "Transport error backing up " + pkgName, e); EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; } finally { try { if (backupData != null) backupData.close(); } catch (IOException e) {} } @@ -2533,7 +2533,7 @@ public class BackupManagerService extends IBackupManager.Stub { // If we encountered an error here it's a transport-level failure. That // means we need to halt everything and reschedule everything for next time. final BackupState nextState; - if (mStatus != BackupConstants.TRANSPORT_OK) { + if (mStatus != BackupTransport.TRANSPORT_OK) { revertAndEndBackup(); nextState = BackupState.FINAL; } else { @@ -4847,7 +4847,7 @@ public class BackupManagerService extends IBackupManager.Stub { mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); // Assume error until we successfully init everything - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; try { // TODO: Log this before getAvailableRestoreSets, somehow @@ -4902,7 +4902,7 @@ public class BackupManagerService extends IBackupManager.Stub { return; } - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; executeNextState(RestoreState.DOWNLOAD_DATA); } @@ -4917,7 +4917,7 @@ public class BackupManagerService extends IBackupManager.Stub { try { mStatus = mTransport.startRestore(mToken, mRestorePackages.toArray(new PackageInfo[0])); - if (mStatus != BackupConstants.TRANSPORT_OK) { + if (mStatus != BackupTransport.TRANSPORT_OK) { Slog.e(TAG, "Error starting restore operation"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); executeNextState(RestoreState.FINAL); @@ -4926,7 +4926,7 @@ public class BackupManagerService extends IBackupManager.Stub { } catch (RemoteException e) { Slog.e(TAG, "Error communicating with transport for restore"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; executeNextState(RestoreState.FINAL); return; } @@ -4941,14 +4941,14 @@ public class BackupManagerService extends IBackupManager.Stub { if (packageName == null) { Slog.e(TAG, "Error getting first restore package"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; executeNextState(RestoreState.FINAL); return; } else if (packageName.equals("")) { Slog.i(TAG, "No restore data available"); int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis); - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; executeNextState(RestoreState.FINAL); return; } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) { @@ -4979,7 +4979,7 @@ public class BackupManagerService extends IBackupManager.Stub { Slog.e(TAG, "No restore metadata available, so not restoring settings"); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL, "Package manager restore metadata missing"); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); executeNextState(RestoreState.FINAL); return; @@ -4987,7 +4987,7 @@ public class BackupManagerService extends IBackupManager.Stub { } catch (RemoteException e) { Slog.e(TAG, "Error communicating with transport for restore"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); executeNextState(RestoreState.FINAL); return; @@ -5118,7 +5118,7 @@ public class BackupManagerService extends IBackupManager.Stub { } } catch (RemoteException e) { Slog.e(TAG, "Unable to fetch restore data from transport"); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; executeNextState(RestoreState.FINAL); } } @@ -5206,7 +5206,7 @@ public class BackupManagerService extends IBackupManager.Stub { Slog.e(TAG, "SElinux restorecon failed for " + downloadFile); } - if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) { + if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { // Transport-level failure, so we wind everything up and // terminate the restore operation. Slog.e(TAG, "Error getting restore data for " + packageName); @@ -5450,12 +5450,12 @@ public class BackupManagerService extends IBackupManager.Stub { long startRealtime = SystemClock.elapsedRealtime(); int status = transport.initializeDevice(); - if (status == BackupConstants.TRANSPORT_OK) { + if (status == BackupTransport.TRANSPORT_OK) { status = transport.finishBackup(); } // Okay, the wipe really happened. Clean up our local bookkeeping. - if (status == BackupConstants.TRANSPORT_OK) { + if (status == BackupTransport.TRANSPORT_OK) { Slog.i(TAG, "Device init successful"); int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d7a19ad..b2b4217 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -38,7 +38,6 @@ import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; -import static android.net.ConnectivityServiceProtocol.NetworkFactoryProtocol; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; @@ -80,6 +79,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; +import android.net.NetworkFactory; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkState; @@ -258,17 +258,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private NetworkStateTracker mNetTrackers[]; - /** - * Holds references to all NetworkAgentInfos claiming to support the legacy - * NetworkType. We used to have a static set of of NetworkStateTrackers - * for each network type. This is the new model. - * Supports synchronous inspection of state. - * These are built out at startup such that an unsupported network - * doesn't get an ArrayList instance, making this a tristate: - * unsupported, supported but not active and active. - */ - private ArrayList<NetworkAgentInfo> mNetworkAgentInfoForType[]; - /* Handles captive portal check on a network */ private CaptivePortalTracker mCaptivePortalTracker; @@ -516,6 +505,118 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int UID_UNUSED = -1; + /** + * Implements support for the legacy "one network per network type" model. + * + * We used to have a static array of NetworkStateTrackers, one for each + * network type, but that doesn't work any more now that we can have, + * for example, more that one wifi network. This class stores all the + * NetworkAgentInfo objects that support a given type, but the legacy + * API will only see the first one. + * + * It serves two main purposes: + * + * 1. Provide information about "the network for a given type" (since this + * API only supports one). + * 2. Send legacy connectivity change broadcasts. Broadcasts are sent if + * the first network for a given type changes, or if the default network + * changes. + */ + private class LegacyTypeTracker { + /** + * Array of lists, one per legacy network type (e.g., TYPE_MOBILE_MMS). + * Each list holds references to all NetworkAgentInfos that are used to + * satisfy requests for that network type. + * + * This array is built out at startup such that an unsupported network + * doesn't get an ArrayList instance, making this a tristate: + * unsupported, supported but not active and active. + * + * The actual lists are populated when we scan the network types that + * are supported on this device. + */ + private ArrayList<NetworkAgentInfo> mTypeLists[]; + + public LegacyTypeTracker() { + mTypeLists = (ArrayList<NetworkAgentInfo>[]) + new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; + } + + public void addSupportedType(int type) { + if (mTypeLists[type] != null) { + throw new IllegalStateException( + "legacy list for type " + type + "already initialized"); + } + mTypeLists[type] = new ArrayList<NetworkAgentInfo>(); + } + + private boolean isDefaultNetwork(NetworkAgentInfo nai) { + return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai; + } + + public boolean isTypeSupported(int type) { + return isNetworkTypeValid(type) && mTypeLists[type] != null; + } + + public NetworkAgentInfo getNetworkForType(int type) { + if (isTypeSupported(type) && !mTypeLists[type].isEmpty()) { + return mTypeLists[type].get(0); + } else { + return null; + } + } + + public void add(int type, NetworkAgentInfo nai) { + if (!isTypeSupported(type)) { + return; // Invalid network type. + } + if (VDBG) log("Adding agent " + nai + " for legacy network type " + type); + + ArrayList<NetworkAgentInfo> list = mTypeLists[type]; + if (list.contains(nai)) { + loge("Attempting to register duplicate agent for type " + type + ": " + nai); + return; + } + + if (list.isEmpty() || isDefaultNetwork(nai)) { + if (VDBG) log("Sending connected broadcast for type " + type + + "isDefaultNetwork=" + isDefaultNetwork(nai)); + sendLegacyNetworkBroadcast(nai, true, type); + } + list.add(nai); + } + + public void remove(NetworkAgentInfo nai) { + if (VDBG) log("Removing agent " + nai); + for (int type = 0; type < mTypeLists.length; type++) { + ArrayList<NetworkAgentInfo> list = mTypeLists[type]; + if (list == null || list.isEmpty()) { + continue; + } + + boolean wasFirstNetwork = false; + if (list.get(0).equals(nai)) { + // This network was the first in the list. Send broadcast. + wasFirstNetwork = true; + } + list.remove(nai); + + if (wasFirstNetwork || isDefaultNetwork(nai)) { + if (VDBG) log("Sending disconnected broadcast for type " + type + + "isDefaultNetwork=" + isDefaultNetwork(nai)); + sendLegacyNetworkBroadcast(nai, false, type); + } + + if (!list.isEmpty() && wasFirstNetwork) { + if (VDBG) log("Other network available for type " + type + + ", sending connected broadcast"); + sendLegacyNetworkBroadcast(list.get(0), false, type); + } + } + } + } + private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(); + public ConnectivityService(Context context, INetworkManagementService netd, INetworkStatsService statsService, INetworkPolicyManager policyManager) { // Currently, omitting a NetworkFactory will create one internally @@ -531,7 +632,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - mDefaultRequest = new NetworkRequest(netCap, true, nextNetworkRequestId()); + mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId()); NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(), NetworkRequestInfo.REQUEST); mNetworkRequests.put(mDefaultRequest, nri); @@ -587,9 +688,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTransitionWakeLockTimeout = mContext.getResources().getInteger( com.android.internal.R.integer.config_networkTransitionTimeout); - mNetworkAgentInfoForType = (ArrayList<NetworkAgentInfo>[]) - new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; - mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1]; @@ -644,7 +742,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { "radio " + n.radio + " in network type " + n.type); continue; } - mNetworkAgentInfoForType[n.type] = new ArrayList<NetworkAgentInfo>(); + mLegacyTypeTracker.addSupportedType(n.type); mNetConfigs[n.type] = n; mNetworksDefined++; @@ -2843,7 +2941,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - private int getRestoreDefaultNetworkDelay(int networkType) { + @Override + public int getRestoreDefaultNetworkDelay(int networkType) { String restoreDefaultNetworkDelayStr = SystemProperties.get( NETWORK_RESTORE_DELAY_PROP_NAME); if(restoreDefaultNetworkDelayStr != null && @@ -2994,6 +3093,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { updateNetworkInfo(nai, info); break; } + case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: { + NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); + if (nai == null) { + loge("EVENT_NETWORK_SCORE_CHANGED from unknown NetworkAgent"); + break; + } + Integer score = (Integer) msg.obj; + updateNetworkScore(nai, score); + break; + } case NetworkMonitor.EVENT_NETWORK_VALIDATED: { NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj; handleConnectionValidated(nai); @@ -3098,7 +3207,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { for (NetworkRequestInfo nri : mNetworkRequests.values()) { if (nri.isRequest == false) continue; NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); - ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, + ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, (nai != null ? nai.currentScore : 0), 0, nri.request); } } else { @@ -3114,11 +3223,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { } else { loge("Error connecting NetworkAgent"); NetworkAgentInfo nai = mNetworkAgentInfos.remove(msg.replyTo); - try { - mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai); - } catch (NullPointerException e) {} if (nai != null) { mNetworkForNetId.remove(nai.network.netId); + mLegacyTypeTracker.remove(nai); } } } @@ -3137,14 +3244,19 @@ public class ConnectivityService extends IConnectivityManager.Stub { } catch (Exception e) { loge("Exception removing network: " + e); } + // TODO - if we move the logic to the network agent (have them disconnect + // because they lost all their requests or because their score isn't good) + // then they would disconnect organically, report their new state and then + // disconnect the channel. + if (nai.networkInfo.isConnected()) { + nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, + null, null); + } notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED); mNetworkAgentInfos.remove(msg.replyTo); updateClat(null, nai.linkProperties, nai); - try { - mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai); - } catch (NullPointerException e) {} - + mLegacyTypeTracker.remove(nai); mNetworkForNetId.remove(nai.network.netId); // Since we've lost the network, go through all the requests that // it was satisfying and see if any other factory can satisfy them. @@ -3154,7 +3266,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId); if (VDBG) { log(" checking request " + request + ", currentNetwork = " + - currentNetwork != null ? currentNetwork.name() : "null"); + (currentNetwork != null ? currentNetwork.name() : "null")); } if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) { mNetworkForRequestId.remove(request.requestId); @@ -3203,7 +3315,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { } if (bestNetwork != null) { if (VDBG) log("using " + bestNetwork.name()); - bestNetwork.networkRequests.put(nri.request.requestId, nri.request); + bestNetwork.addRequest(nri.request); + int legacyType = nri.request.legacyType; + if (legacyType != TYPE_NONE) { + mLegacyTypeTracker.add(legacyType, bestNetwork); + } notifyNetworkCallback(bestNetwork, nri); score = bestNetwork.currentScore; } @@ -3211,7 +3327,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) { if (DBG) log("sending new NetworkRequest to factories"); for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request); + nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, + 0, nri.request); } } } @@ -3233,7 +3350,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (nri.isRequest) { for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request); + nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, + nri.request); } if (affectedNetwork != null) { @@ -5279,7 +5397,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, int timeoutSec, IBinder binder) { + Messenger messenger, int timeoutSec, IBinder binder, int legacyType) { if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) == false) { enforceConnectivityInternalPermission(); @@ -5291,7 +5409,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { throw new IllegalArgumentException("Bad timeout specified"); } NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities( - networkCapabilities), false, nextNetworkRequestId()); + networkCapabilities), legacyType, nextNetworkRequestId()); if (DBG) log("requestNetwork for " + networkRequest); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, NetworkRequestInfo.REQUEST); @@ -5317,7 +5435,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { enforceAccessPermission(); NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities( - networkCapabilities), false, nextNetworkRequestId()); + networkCapabilities), TYPE_NONE, nextNetworkRequestId()); if (DBG) log("listenForNetwork for " + networkRequest); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, NetworkRequestInfo.LISTEN); @@ -5392,18 +5510,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(), new NetworkInfo(networkInfo), new LinkProperties(linkProperties), new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler); - + if (VDBG) log("registerNetworkAgent " + nai); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai)); } private void handleRegisterNetworkAgent(NetworkAgentInfo na) { if (VDBG) log("Got NetworkAgent Messenger"); mNetworkAgentInfos.put(na.messenger, na); - try { - mNetworkAgentInfoForType[na.networkInfo.getType()].add(na); - } catch (NullPointerException e) { - loge("registered NetworkAgent for unsupported type: " + na); - } mNetworkForNetId.put(na.network.netId, na); na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger); NetworkInfo networkInfo = na.networkInfo; @@ -5439,7 +5552,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mClat.stopClat(); } // If the link requires clat to be running, then start the daemon now. - if (newLp != null && na.networkInfo.isConnected()) { + if (na.networkInfo.isConnected()) { mClat.startClat(na); } else { mClat.stopClat(); @@ -5555,7 +5668,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) { if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString()); for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, networkRequest); + nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0, + networkRequest); } } @@ -5658,7 +5772,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (VDBG) log(" accepting network in place of null"); } mNetworkForRequestId.put(nri.request.requestId, newNetwork); - newNetwork.networkRequests.put(nri.request.requestId, nri.request); + newNetwork.addRequest(nri.request); + int legacyType = nri.request.legacyType; + if (legacyType != TYPE_NONE) { + mLegacyTypeTracker.add(legacyType, newNetwork); + } keep = true; // TODO - this could get expensive if we have alot of requests for this // network. Think about if there is a way to reduce this. Push @@ -5672,6 +5790,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } else { setDefaultDnsSystemProperties(new ArrayList<InetAddress>()); } + mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork); } } } @@ -5792,6 +5911,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + private void updateNetworkScore(NetworkAgentInfo nai, Integer scoreInteger) { + int score = scoreInteger.intValue(); + // TODO + } + // notify only this one new request of the current state protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) { int notifyType = ConnectivityManager.CALLBACK_AVAILABLE; @@ -5801,93 +5925,88 @@ public class ConnectivityService extends IConnectivityManager.Stub { // } else if (nai.networkMonitor.isEvaluating()) { // notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType); // } - if (nri.request.needsBroadcasts) { - // TODO -// sendNetworkBroadcast(nai, notifyType); - } callCallbackForRequest(nri, nai, notifyType); } + private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) { + if (connected) { + NetworkInfo info = new NetworkInfo(nai.networkInfo); + info.setType(type); + sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay()); + } else { + NetworkInfo info = new NetworkInfo(nai.networkInfo); + info.setType(type); + Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); + if (info.isFailover()) { + intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); + nai.networkInfo.setFailover(false); + } + if (info.getReason() != null) { + intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); + } + if (info.getExtraInfo() != null) { + intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); + } + NetworkAgentInfo newDefaultAgent = null; + if (nai.networkRequests.get(mDefaultRequest.requestId) != null) { + newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId); + if (newDefaultAgent != null) { + intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, + newDefaultAgent.networkInfo); + } else { + intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); + } + } + intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, + mDefaultInetConditionPublished); + final Intent immediateIntent = new Intent(intent); + immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); + sendStickyBroadcast(immediateIntent); + sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay()); + if (newDefaultAgent != null) { + sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo, + getConnectivityChangeDelay()); + } + } + } + protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) { if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name()); - boolean needsBroadcasts = false; for (int i = 0; i < networkAgent.networkRequests.size(); i++) { NetworkRequest nr = networkAgent.networkRequests.valueAt(i); NetworkRequestInfo nri = mNetworkRequests.get(nr); if (VDBG) log(" sending notification for " + nr); - if (nr.needsBroadcasts) needsBroadcasts = true; callCallbackForRequest(nri, networkAgent, notifyType); } - if (needsBroadcasts) { - if (notifyType == ConnectivityManager.CALLBACK_AVAILABLE) { - sendConnectedBroadcastDelayed(networkAgent.networkInfo, - getConnectivityChangeDelay()); - } else if (notifyType == ConnectivityManager.CALLBACK_LOST) { - NetworkInfo info = new NetworkInfo(networkAgent.networkInfo); - Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); - if (info.isFailover()) { - intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); - networkAgent.networkInfo.setFailover(false); - } - if (info.getReason() != null) { - intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); - } - if (info.getExtraInfo() != null) { - intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); - } - NetworkAgentInfo newDefaultAgent = null; - if (networkAgent.networkRequests.get(mDefaultRequest.requestId) != null) { - newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId); - if (newDefaultAgent != null) { - intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, - newDefaultAgent.networkInfo); - } else { - intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); - } - } - intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, - mDefaultInetConditionPublished); - final Intent immediateIntent = new Intent(intent); - immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); - sendStickyBroadcast(immediateIntent); - sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay()); - if (newDefaultAgent != null) { - sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo, - getConnectivityChangeDelay()); - } - } - } } private LinkProperties getLinkPropertiesForTypeInternal(int networkType) { - ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; - if (list == null) return null; - try { - return new LinkProperties(list.get(0).linkProperties); - } catch (IndexOutOfBoundsException e) { - return new LinkProperties(); - } + NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + return (nai != null) ? + new LinkProperties(nai.linkProperties) : + new LinkProperties(); } private NetworkInfo getNetworkInfoForType(int networkType) { - ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; - if (list == null) return null; - try { - return new NetworkInfo(list.get(0).networkInfo); - } catch (IndexOutOfBoundsException e) { - return new NetworkInfo(networkType, 0, "Unknown", ""); + if (!mLegacyTypeTracker.isTypeSupported(networkType)) + return null; + + NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + if (nai != null) { + NetworkInfo result = new NetworkInfo(nai.networkInfo); + result.setType(networkType); + return result; + } else { + return new NetworkInfo(networkType, 0, "Unknown", ""); } } private NetworkCapabilities getNetworkCapabilitiesForType(int networkType) { - ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; - if (list == null) return null; - try { - return new NetworkCapabilities(list.get(0).networkCapabilities); - } catch (IndexOutOfBoundsException e) { - return new NetworkCapabilities(); - } + NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + return (nai != null) ? + new NetworkCapabilities(nai.networkCapabilities) : + new NetworkCapabilities(); } } diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java index 4a8bf72..af38664 100644 --- a/services/core/java/com/android/server/DockObserver.java +++ b/services/core/java/com/android/server/DockObserver.java @@ -19,10 +19,12 @@ package com.android.server; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; +import android.os.Binder; import android.os.Handler; import android.os.Message; import android.os.PowerManager; @@ -33,62 +35,62 @@ import android.provider.Settings; import android.util.Log; import android.util.Slog; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileReader; +import java.io.PrintWriter; /** - * <p>DockObserver monitors for a docking station. + * DockObserver monitors for a docking station. */ -final class DockObserver extends UEventObserver { - private static final String TAG = DockObserver.class.getSimpleName(); +final class DockObserver extends SystemService { + private static final String TAG = "DockObserver"; private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; private static final int MSG_DOCK_STATE_CHANGED = 0; - private final Object mLock = new Object(); + private final PowerManager mPowerManager; + private final PowerManager.WakeLock mWakeLock; - private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; - private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + private final Object mLock = new Object(); private boolean mSystemReady; - private final Context mContext; - private final PowerManager mPowerManager; - private final PowerManager.WakeLock mWakeLock; + private int mActualDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + + private int mReportedDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + + private boolean mUpdatesStopped; public DockObserver(Context context) { - mContext = context; + super(context); - mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); init(); // set initial status - startObserving(DOCK_UEVENT_MATCH); + + mObserver.startObserving(DOCK_UEVENT_MATCH); } @Override - public void onUEvent(UEventObserver.UEvent event) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Slog.v(TAG, "Dock UEVENT: " + event.toString()); - } + public void onStart() { + publishBinderService(TAG, new BinderService()); + } - synchronized (mLock) { - try { - int newState = Integer.parseInt(event.get("SWITCH_STATE")); - if (newState != mDockState) { - mPreviousDockState = mDockState; - mDockState = newState; - if (mSystemReady) { - // Wake up immediately when docked or undocked. - mPowerManager.wakeUp(SystemClock.uptimeMillis()); - - updateLocked(); - } + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_ACTIVITY_MANAGER_READY) { + synchronized (mLock) { + mSystemReady = true; + + // don't bother broadcasting undocked here + if (mReportedDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { + updateLocked(); } - } catch (NumberFormatException e) { - Slog.e(TAG, "Could not parse switch state from event " + event); } } } @@ -100,8 +102,8 @@ final class DockObserver extends UEventObserver { FileReader file = new FileReader(DOCK_STATE_PATH); try { int len = file.read(buffer, 0, 1024); - mDockState = Integer.valueOf((new String(buffer, 0, len)).trim()); - mPreviousDockState = mDockState; + setActualDockStateLocked(Integer.valueOf((new String(buffer, 0, len)).trim())); + mPreviousDockState = mActualDockState; } finally { file.close(); } @@ -113,13 +115,21 @@ final class DockObserver extends UEventObserver { } } - void systemReady() { - synchronized (mLock) { - // don't bother broadcasting undocked here - if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { + private void setActualDockStateLocked(int newState) { + mActualDockState = newState; + if (!mUpdatesStopped) { + setDockStateLocked(newState); + } + } + + private void setDockStateLocked(int newState) { + if (newState != mReportedDockState) { + mReportedDockState = newState; + if (mSystemReady) { + // Wake up immediately when docked or undocked. + mPowerManager.wakeUp(SystemClock.uptimeMillis()); updateLocked(); } - mSystemReady = true; } } @@ -130,10 +140,13 @@ final class DockObserver extends UEventObserver { private void handleDockStateChange() { synchronized (mLock) { - Slog.i(TAG, "Dock state changed: " + mDockState); + Slog.i(TAG, "Dock state changed from " + mPreviousDockState + " to " + + mReportedDockState); + final int previousDockState = mPreviousDockState; + mPreviousDockState = mReportedDockState; // Skip the dock intent if not yet provisioned. - final ContentResolver cr = mContext.getContentResolver(); + final ContentResolver cr = getContext().getContentResolver(); if (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) == 0) { Slog.i(TAG, "Device not provisioned, skipping dock broadcast"); @@ -143,27 +156,27 @@ final class DockObserver extends UEventObserver { // Pack up the values and broadcast them to everyone Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); + intent.putExtra(Intent.EXTRA_DOCK_STATE, mReportedDockState); // Play a sound to provide feedback to confirm dock connection. // Particularly useful for flaky contact pins... if (Settings.Global.getInt(cr, Settings.Global.DOCK_SOUNDS_ENABLED, 1) == 1) { String whichSound = null; - if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { - if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) || - (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || - (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { + if (mReportedDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { + if ((previousDockState == Intent.EXTRA_DOCK_STATE_DESK) || + (previousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || + (previousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { whichSound = Settings.Global.DESK_UNDOCK_SOUND; - } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) { + } else if (previousDockState == Intent.EXTRA_DOCK_STATE_CAR) { whichSound = Settings.Global.CAR_UNDOCK_SOUND; } } else { - if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) || - (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || - (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { + if ((mReportedDockState == Intent.EXTRA_DOCK_STATE_DESK) || + (mReportedDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || + (mReportedDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) { whichSound = Settings.Global.DESK_DOCK_SOUND; - } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { + } else if (mReportedDockState == Intent.EXTRA_DOCK_STATE_CAR) { whichSound = Settings.Global.CAR_DOCK_SOUND; } } @@ -173,7 +186,8 @@ final class DockObserver extends UEventObserver { if (soundPath != null) { final Uri soundUri = Uri.parse("file://" + soundPath); if (soundUri != null) { - final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); + final Ringtone sfx = RingtoneManager.getRingtone( + getContext(), soundUri); if (sfx != null) { sfx.setStreamType(AudioManager.STREAM_SYSTEM); sfx.play(); @@ -186,7 +200,7 @@ final class DockObserver extends UEventObserver { // Send the dock event intent. // There are many components in the system watching for this so as to // adjust audio routing, screen orientation, etc. - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); // Release the wake lock that was acquired when the message was posted. mWakeLock.release(); @@ -203,4 +217,71 @@ final class DockObserver extends UEventObserver { } } }; + + private final UEventObserver mObserver = new UEventObserver() { + @Override + public void onUEvent(UEventObserver.UEvent event) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Slog.v(TAG, "Dock UEVENT: " + event.toString()); + } + + try { + synchronized (mLock) { + setActualDockStateLocked(Integer.parseInt(event.get("SWITCH_STATE"))); + } + } catch (NumberFormatException e) { + Slog.e(TAG, "Could not parse switch state from event " + event); + } + } + }; + + private final class BinderService extends Binder { + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump dock observer service from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + if (args == null || args.length == 0 || "-a".equals(args[0])) { + pw.println("Current Dock Observer Service state:"); + if (mUpdatesStopped) { + pw.println(" (UPDATES STOPPED -- use 'reset' to restart)"); + } + pw.println(" reported state: " + mReportedDockState); + pw.println(" previous state: " + mPreviousDockState); + pw.println(" actual state: " + mActualDockState); + } else if (args.length == 3 && "set".equals(args[0])) { + String key = args[1]; + String value = args[2]; + try { + if ("state".equals(key)) { + mUpdatesStopped = true; + setDockStateLocked(Integer.parseInt(value)); + } else { + pw.println("Unknown set option: " + key); + } + } catch (NumberFormatException ex) { + pw.println("Bad value: " + value); + } + } else if (args.length == 1 && "reset".equals(args[0])) { + mUpdatesStopped = false; + setDockStateLocked(mActualDockState); + } else { + pw.println("Dump current dock state, or:"); + pw.println(" set state <value>"); + pw.println(" reset"); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } } diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index fb69c86..3a8f767 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -1681,6 +1681,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurMethodId = null; unbindCurrentMethodLocked(true, false); } + // Here is not the perfect place to reset the switching controller. Ideally + // mSwitchingController and mSettings should be able to share the same state. + // TODO: Make sure that mSwitchingController and mSettings are sharing the + // the same enabled IMEs list. + mSwitchingController.resetCircularListLocked(mContext); } /* package */ void setInputMethodLocked(String id, int subtypeId) { @@ -2650,7 +2655,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub setInputMethodEnabledLocked(defaultImiId, true); } } - + // Here is not the perfect place to reset the switching controller. Ideally + // mSwitchingController and mSettings should be able to share the same state. + // TODO: Make sure that mSwitchingController and mSettings are sharing the + // the same enabled IMEs list. mSwitchingController.resetCircularListLocked(mContext); } diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java index cd8c13f..d8ee8a1 100644 --- a/services/core/java/com/android/server/WiredAccessoryManager.java +++ b/services/core/java/com/android/server/WiredAccessoryManager.java @@ -246,7 +246,8 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { private void setDeviceStateLocked(int headset, int headsetState, int prevHeadsetState, String headsetName) { if ((headsetState & headset) != (prevHeadsetState & headset)) { - int device; + int outDevice = 0; + int inDevice = 0; int state; if ((headsetState & headset) != 0) { @@ -256,15 +257,16 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { } if (headset == BIT_HEADSET) { - device = AudioManager.DEVICE_OUT_WIRED_HEADSET; + outDevice = AudioManager.DEVICE_OUT_WIRED_HEADSET; + inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET; } else if (headset == BIT_HEADSET_NO_MIC){ - device = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; + outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE; } else if (headset == BIT_USB_HEADSET_ANLG) { - device = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; + outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET; } else if (headset == BIT_USB_HEADSET_DGTL) { - device = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET; + outDevice = AudioManager.DEVICE_OUT_DGTL_DOCK_HEADSET; } else if (headset == BIT_HDMI_AUDIO) { - device = AudioManager.DEVICE_OUT_HDMI; + outDevice = AudioManager.DEVICE_OUT_HDMI; } else { Slog.e(TAG, "setDeviceState() invalid headset type: "+headset); return; @@ -273,7 +275,12 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks { if (LOG) Slog.v(TAG, "device "+headsetName+((state == 1) ? " connected" : " disconnected")); - mAudioManager.setWiredDeviceConnectionState(device, state, headsetName); + if (outDevice != 0) { + mAudioManager.setWiredDeviceConnectionState(outDevice, state, headsetName); + } + if (inDevice != 0) { + mAudioManager.setWiredDeviceConnectionState(inDevice, state, headsetName); + } } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 108a079..816e022 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -309,7 +309,7 @@ public final class ActiveServices { } ServiceRecord r = res.record; NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked( - callingUid, r.packageName, service, service.getFlags(), null); + callingUid, r.packageName, service, service.getFlags(), null, r.userId); if (unscheduleServiceRestartLocked(r, callingUid, false)) { if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r); } @@ -989,7 +989,8 @@ public final class ActiveServices { sInfo.applicationInfo.packageName, sInfo.name); if (userId > 0) { if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo, - sInfo.name, sInfo.flags)) { + sInfo.name, sInfo.flags) + && mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) { userId = 0; smap = getServiceMap(0); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index fc808ec..aede797 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -283,7 +284,7 @@ public final class ActivityManagerService extends ActivityManagerNative // before we decide it's never going to come up for real, when the process was // started with a wrapper for instrumentation (such as Valgrind) because it // could take much longer than usual. - static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000; + static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000; // How long to wait after going idle before forcing apps to GC. static final int GC_TIMEOUT = 5*1000; @@ -2189,7 +2190,7 @@ public final class ActivityManagerService extends ActivityManagerNative mBatteryStatsService.publish(mContext); mUsageStatsService.publish(mContext); mAppOpsService.publish(mContext); - + Slog.d("AppOps", "AppOpsService published"); LocalServices.addService(ActivityManagerInternal.class, new LocalService()); } @@ -2833,7 +2834,7 @@ public final class ActivityManagerService extends ActivityManagerNative return app; } - startProcessLocked(app, hostingType, hostingNameStr); + startProcessLocked(app, hostingType, hostingNameStr, null /* ABI override */); return (app.pid != 0) ? app : null; } @@ -2842,7 +2843,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private final void startProcessLocked(ProcessRecord app, - String hostingType, String hostingNameStr) { + String hostingType, String hostingNameStr, String abiOverride) { if (app.pid > 0 && app.pid != MY_PID) { synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(app.pid); @@ -2927,7 +2928,7 @@ public final class ActivityManagerService extends ActivityManagerNative debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } - String requiredAbi = app.info.cpuAbi; + String requiredAbi = (abiOverride != null) ? abiOverride : app.info.cpuAbi; if (requiredAbi == null) { requiredAbi = Build.SUPPORTED_ABIS[0]; } @@ -2977,6 +2978,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } buf.append("}"); + if (requiredAbi != null) { + buf.append(" abi="); + buf.append(requiredAbi); + } Slog.i(TAG, buf.toString()); app.setPid(startResult.pid); app.usingWrapper = startResult.usingWrapper; @@ -5019,7 +5024,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.persistent && !app.isolated) { if (!callerWillRestart) { - addAppLocked(app.info, false); + addAppLocked(app.info, false, null /* ABI override */); } else { needRestart = true; } @@ -5132,7 +5137,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.deathRecipient = adr; } catch (RemoteException e) { app.resetPackageList(mProcessStats); - startProcessLocked(app, "link fail", processName); + startProcessLocked(app, "link fail", processName, null /* ABI override */); return false; } @@ -5225,7 +5230,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.resetPackageList(mProcessStats); app.unlinkDeathRecipient(); - startProcessLocked(app, "bind fail", processName); + startProcessLocked(app, "bind fail", processName, null /* ABI override */); return false; } @@ -5376,7 +5381,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int ip=0; ip<NP; ip++) { if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: " + procs.get(ip)); - startProcessLocked(procs.get(ip), "on-hold", null); + startProcessLocked(procs.get(ip), "on-hold", null, null /* ABI override */); } } @@ -6016,6 +6021,9 @@ public final class ActivityManagerService extends ActivityManagerNative IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid); + if (UserHandle.getUserId(uid) != grantUri.sourceUserId) { + return false; + } if (pi.applicationInfo.uid == uid) { return true; @@ -6377,7 +6385,7 @@ public final class ActivityManagerService extends ActivityManagerNative * Like checkGrantUriPermissionLocked, but takes an Intent. */ NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid, - String targetPkg, Intent intent, int mode, NeededUriGrants needed) { + String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) { if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking URI perm to data=" + (intent != null ? intent.getData() : null) + " clip=" + (intent != null ? intent.getClipData() : null) @@ -6396,11 +6404,28 @@ public final class ActivityManagerService extends ActivityManagerNative if (data == null && clip == null) { return null; } - + final IPackageManager pm = AppGlobals.getPackageManager(); + int targetUid; + if (needed != null) { + targetUid = needed.targetUid; + } else { + try { + targetUid = pm.getPackageUid(targetPkg, targetUserId); + } catch (RemoteException ex) { + return null; + } + if (targetUid < 0) { + if (DEBUG_URI_PERMISSION) { + Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg + + " on user " + targetUserId); + } + return null; + } + } if (data != null) { GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), data); - int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode, - needed != null ? needed.targetUid : -1); + targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode, + targetUid); if (targetUid > 0) { if (needed == null) { needed = new NeededUriGrants(targetPkg, targetUid, mode); @@ -6412,10 +6437,9 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<clip.getItemCount(); i++) { Uri uri = clip.getItemAt(i).getUri(); if (uri != null) { - int targetUid = -1; GrantUri grantUri = GrantUri.resolve(UserHandle.getUserId(callingUid), uri); targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode, - needed != null ? needed.targetUid : -1); + targetUid); if (targetUid > 0) { if (needed == null) { needed = new NeededUriGrants(targetPkg, targetUid, mode); @@ -6426,7 +6450,7 @@ public final class ActivityManagerService extends ActivityManagerNative Intent clipIntent = clip.getItemAt(i).getIntent(); if (clipIntent != null) { NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentLocked( - callingUid, targetPkg, clipIntent, mode, needed); + callingUid, targetPkg, clipIntent, mode, needed, targetUserId); if (newNeeded != null) { needed = newNeeded; } @@ -6453,9 +6477,9 @@ public final class ActivityManagerService extends ActivityManagerNative } void grantUriPermissionFromIntentLocked(int callingUid, - String targetPkg, Intent intent, UriPermissionOwner owner) { + String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) { NeededUriGrants needed = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg, - intent, intent != null ? intent.getFlags() : 0, null); + intent, intent != null ? intent.getFlags() : 0, null, targetUserId); if (needed == null) { return; } @@ -7734,7 +7758,7 @@ public final class ActivityManagerService extends ActivityManagerNative * in {@link ContentProvider}. */ private final String checkContentProviderPermissionLocked( - ProviderInfo cpi, ProcessRecord r, int userId) { + ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser) { final int callingPid = (r != null) ? r.pid : Binder.getCallingPid(); final int callingUid = (r != null) ? r.uid : Binder.getCallingUid(); final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid); @@ -7751,8 +7775,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - userId = handleIncomingUser(callingPid, callingUid, userId, - false, true, "checkContentProviderPermissionLocked", null); + if (checkUser) { + userId = handleIncomingUser(callingPid, callingUid, userId, + false, true, "checkContentProviderPermissionLocked " + cpi.authority, null); + } if (checkComponentPermission(cpi.readPermission, callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported) == PackageManager.PERMISSION_GRANTED) { @@ -7887,13 +7913,34 @@ public final class ActivityManagerService extends ActivityManagerNative } } + boolean checkCrossUser = true; + // First check if this content provider has been published... cpr = mProviderMap.getProviderByName(name, userId); + // If that didn't work, check if it exists for user 0 and then + // verify that it's a singleton provider before using it. + if (cpr == null && userId != UserHandle.USER_OWNER) { + cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER); + if (cpr != null) { + cpi = cpr.info; + if (isSingleton(cpi.processName, cpi.applicationInfo, + cpi.name, cpi.flags) + && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) { + userId = UserHandle.USER_OWNER; + checkCrossUser = false; + } else { + cpr = null; + cpi = null; + } + } + } + boolean providerRunning = cpr != null; if (providerRunning) { cpi = cpr.info; String msg; - if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) { + if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) + != null) { throw new SecurityException(msg); } @@ -7973,15 +8020,21 @@ public final class ActivityManagerService extends ActivityManagerNative if (cpi == null) { return null; } + // If the provider is a singleton AND + // (it's a call within the same user || the provider is a + // privileged app) + // Then allow connecting to the singleton provider singleton = isSingleton(cpi.processName, cpi.applicationInfo, - cpi.name, cpi.flags); + cpi.name, cpi.flags) + && isValidSingletonCall(r.uid, cpi.applicationInfo.uid); if (singleton) { - userId = 0; + userId = UserHandle.USER_OWNER; } cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId); String msg; - if ((msg=checkContentProviderPermissionLocked(cpi, r, userId)) != null) { + if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton)) + != null) { throw new SecurityException(msg); } @@ -8517,7 +8570,8 @@ public final class ActivityManagerService extends ActivityManagerNative return new ProcessRecord(stats, info, proc, uid); } - final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) { + final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated, + String abiOverride) { ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(info.processName, info.uid, true); @@ -8552,7 +8606,8 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); - startProcessLocked(app, "added application", app.processName); + startProcessLocked(app, "added application", app.processName, + abiOverride); } return app; @@ -9769,7 +9824,7 @@ public final class ActivityManagerService extends ActivityManagerNative = (ApplicationInfo)apps.get(i); if (info != null && !info.packageName.equals("android")) { - addAppLocked(info, false); + addAppLocked(info, false, null /* ABI override */); } } } @@ -10560,8 +10615,7 @@ public final class ActivityManagerService extends ActivityManagerNative // assume our apps are happy - lazy create the list List<ActivityManager.ProcessErrorStateInfo> errList = null; - final boolean allUsers = ActivityManager.checkUidPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED; int userId = UserHandle.getUserId(Binder.getCallingUid()); @@ -10650,8 +10704,7 @@ public final class ActivityManagerService extends ActivityManagerNative enforceNotIsolatedCaller("getRunningAppProcesses"); // Lazy instantiation of list List<ActivityManager.RunningAppProcessInfo> runList = null; - final boolean allUsers = ActivityManager.checkUidPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED; int userId = UserHandle.getUserId(Binder.getCallingUid()); synchronized (this) { @@ -12951,7 +13004,7 @@ public final class ActivityManagerService extends ActivityManagerNative // We have components that still need to be running in the // process, so re-launch it. mProcessNames.put(app.processName, app.uid, app); - startProcessLocked(app, "restart", app.processName); + startProcessLocked(app, "restart", app.processName, null /* ABI override */); } else if (app.pid > 0 && app.pid != MY_PID) { // Goodbye! boolean removed; @@ -13091,8 +13144,7 @@ public final class ActivityManagerService extends ActivityManagerNative if ((requireFull || checkComponentPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) - && checkComponentPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + && checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) { if (userId == UserHandle.USER_CURRENT_OR_SELF) { @@ -13112,7 +13164,7 @@ public final class ActivityManagerService extends ActivityManagerNative builder.append(" but is calling from user "); builder.append(UserHandle.getUserId(callingUid)); builder.append("; this requires "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + builder.append(INTERACT_ACROSS_USERS_FULL); if (!requireFull) { builder.append(" or "); builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); @@ -13142,8 +13194,9 @@ public final class ActivityManagerService extends ActivityManagerNative boolean isSingleton(String componentProcessName, ApplicationInfo aInfo, String className, int flags) { boolean result = false; + // For apps that don't have pre-defined UIDs, check for permission if (UserHandle.getAppId(aInfo.uid) >= Process.FIRST_APPLICATION_UID) { - if ((flags&ServiceInfo.FLAG_SINGLE_USER) != 0) { + if ((flags & ServiceInfo.FLAG_SINGLE_USER) != 0) { if (ActivityManager.checkUidPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, aInfo.uid) != PackageManager.PERMISSION_GRANTED) { @@ -13154,12 +13207,14 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, msg); throw new SecurityException(msg); } + // Permission passed result = true; } - } else if (componentProcessName == aInfo.packageName) { - result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0; } else if ("system".equals(componentProcessName)) { result = true; + } else { + // App with pre-defined UID, check if it's a persistent app + result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0; } if (DEBUG_MU) { Slog.v(TAG, "isSingleton(" + componentProcessName + ", " + aInfo @@ -13168,6 +13223,21 @@ public final class ActivityManagerService extends ActivityManagerNative return result; } + /** + * Checks to see if the caller is in the same app as the singleton + * component, or the component is in a special app. It allows special apps + * to export singleton components but prevents exporting singleton + * components for regular apps. + */ + boolean isValidSingletonCall(int callingUid, int componentUid) { + int componentAppId = UserHandle.getAppId(componentUid); + return UserHandle.isSameApp(callingUid, componentUid) + || componentAppId == Process.SYSTEM_UID + || componentAppId == Process.PHONE_UID + || ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, componentUid) + == PackageManager.PERMISSION_GRANTED; + } + public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, int userId) { @@ -13714,8 +13784,8 @@ public final class ActivityManagerService extends ActivityManagerNative */ int callingAppId = UserHandle.getAppId(callingUid); if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID - || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID || - callingUid == 0) { + || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID + || callingUid == 0) { // Always okay. } else if (callerApp == null || !callerApp.persistent) { try { @@ -14248,7 +14318,7 @@ public final class ActivityManagerService extends ActivityManagerNative public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection, - int userId) { + int userId, String abiOverride) { enforceNotIsolatedCaller("startInstrumentation"); userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, true, "startInstrumentation", null); @@ -14297,7 +14367,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Instrumentation can kill and relaunch even persistent processes forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId, "start instr"); - ProcessRecord app = addAppLocked(ai, false); + ProcessRecord app = addAppLocked(ai, false, abiOverride); app.instrumentationClass = className; app.instrumentationInfo = ai; app.instrumentationProfileFile = profileFile; @@ -16261,7 +16331,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.persistent) { if (app.persistent) { - addAppLocked(app.info, false); + addAppLocked(app.info, false, null /* ABI override */); } } } @@ -16525,12 +16595,12 @@ public final class ActivityManagerService extends ActivityManagerNative } private boolean startUser(final int userId, boolean foreground) { - if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: switchUser() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + + " requires " + INTERACT_ACROSS_USERS_FULL; Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -16896,12 +16966,12 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public int stopUser(final int userId, final IStopUserCallback callback) { - if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: switchUser() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + + " requires " + INTERACT_ACROSS_USERS_FULL; Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -17039,7 +17109,7 @@ public final class ActivityManagerService extends ActivityManagerNative public UserInfo getCurrentUser() { if ((checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) && ( - checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + checkCallingPermission(INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED)) { String msg = "Permission Denial: getCurrentUser() from pid=" + Binder.getCallingPid() @@ -17125,12 +17195,12 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void registerUserSwitchObserver(IUserSwitchObserver observer) { - if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: registerUserSwitchObserver() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + + " requires " + INTERACT_ACROSS_USERS_FULL; Slog.w(TAG, msg); throw new SecurityException(msg); } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index b429b93..32722bc 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -635,7 +635,7 @@ final class ActivityRecord { final void deliverNewIntentLocked(int callingUid, Intent intent) { // The activity now gets access to the data associated with this Intent. service.grantUriPermissionFromIntentLocked(callingUid, packageName, - intent, getUriPermissionsLocked()); + intent, getUriPermissionsLocked(), userId); // We want to immediately deliver the intent to the activity if // it is currently the top resumed activity... however, if the // device is sleeping, then all activities are stopped, so in that diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 1804d03..ba12374 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2353,7 +2353,7 @@ final class ActivityStack { if (callingUid > 0) { mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName, - data, r.getUriPermissionsLocked()); + data, r.getUriPermissionsLocked(), r.userId); } if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r @@ -2536,10 +2536,15 @@ final class ActivityStack { if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo + " who=" + r.resultWho + " req=" + r.requestCode + " res=" + resultCode + " data=" + resultData); + if (resultTo.userId != r.userId) { + if (resultData != null) { + resultData.prepareToLeaveUser(r.userId); + } + } if (r.info.applicationInfo.uid > 0) { mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid, resultTo.packageName, resultData, - resultTo.getUriPermissionsLocked()); + resultTo.getUriPermissionsLocked(), resultTo.userId); } resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 0cc53d1..dc4ad48 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -45,6 +45,7 @@ import android.app.PendingIntent; import android.app.ActivityManager.RunningTaskInfo; import android.app.IActivityManager.WaitResult; import android.app.ResultInfo; +import android.app.StatusBarManager; import android.content.ComponentName; import android.content.Context; import android.content.IIntentSender; @@ -73,6 +74,7 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.service.voice.IVoiceInteractionSession; @@ -88,11 +90,13 @@ import android.view.Surface; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.TransferPipe; +import com.android.internal.statusbar.IStatusBarService; import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService.PendingActivityLaunch; import com.android.server.am.ActivityStack.ActivityState; import com.android.server.wm.WindowManagerService; + import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; @@ -128,9 +132,15 @@ public final class ActivityStackSupervisor implements DisplayListener { static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6; static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7; static final int CONTAINER_CALLBACK_VISIBILITY = FIRST_SUPERVISOR_STACK_MSG + 8; + static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9; + static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10; private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay"; + /** Status Bar Service **/ + private IBinder mToken = new Binder(); + private IStatusBarService mStatusBarService; + // For debugging to make sure the caller when acquiring/releasing our // wake lock is the system process. static final boolean VALIDATE_WAKE_LOCK_CALLER = false; @@ -253,6 +263,21 @@ public final class ActivityStackSupervisor implements DisplayListener { mLaunchingActivity.setReferenceCounted(false); } + // This function returns a IStatusBarService. The value is from ServiceManager. + // getService and is cached. + private IStatusBarService getStatusBarService() { + synchronized (mService) { + if (mStatusBarService == null) { + mStatusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); + if (mStatusBarService == null) { + Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE"); + } + } + return mStatusBarService; + } + } + void setWindowManager(WindowManagerService wm) { synchronized (mService) { mWindowManager = wm; @@ -1898,7 +1923,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName, - intent, r.getUriPermissionsLocked()); + intent, r.getUriPermissionsLocked(), r.userId); if (newTask) { EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId); @@ -2942,9 +2967,12 @@ public final class ActivityStackSupervisor implements DisplayListener { } void setLockTaskModeLocked(TaskRecord task) { + final Message lockTaskMsg = Message.obtain(); if (task == null) { // Take out of lock task mode. mLockTaskModeTask = null; + lockTaskMsg.what = LOCK_TASK_END_MSG; + mHandler.sendMessage(lockTaskMsg); return; } if (isLockTaskModeViolation(task)) { @@ -2954,6 +2982,8 @@ public final class ActivityStackSupervisor implements DisplayListener { mLockTaskModeTask = task; findTaskToMoveToFrontLocked(task, 0, null); resumeTopActivitiesLocked(); + lockTaskMsg.what = LOCK_TASK_START_MSG; + mHandler.sendMessage(lockTaskMsg); } boolean isLockTaskModeViolation(TaskRecord task) { @@ -3050,6 +3080,32 @@ public final class ActivityStackSupervisor implements DisplayListener { } catch (RemoteException e) { } } + case LOCK_TASK_START_MSG: { + // When lock task starts, we disable the status bars. + try { + if (getStatusBarService() != null) { + getStatusBarService().disable + (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK, + mToken, mService.mContext.getPackageName()); + } + } catch (RemoteException ex) { + throw new RuntimeException(ex); + } + break; + } + case LOCK_TASK_END_MSG: { + // When lock task ends, we enable the status bars. + try { + if (getStatusBarService() != null) { + getStatusBarService().disable + (StatusBarManager.DISABLE_NONE, + mToken, mService.mContext.getPackageName()); + } + } catch (RemoteException ex) { + throw new RuntimeException(ex); + } + break; + } } } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 9d6481a..7b2b04d 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; @@ -858,7 +859,10 @@ public final class BroadcastQueue { r.state = BroadcastRecord.APP_RECEIVE; String targetProcess = info.activityInfo.processName; r.curComponent = component; - if (r.callingUid != Process.SYSTEM_UID && isSingleton) { + final int receiverUid = info.activityInfo.applicationInfo.uid; + // If it's a singleton, it needs to be the same app or a special app + if (r.callingUid != Process.SYSTEM_UID && isSingleton + && mService.isValidSingletonCall(r.callingUid, receiverUid)) { info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0); } r.curReceiver = info.activityInfo; diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 8102591..b03c247 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -45,7 +45,6 @@ public class NetworkAgentInfo { public int currentScore; public final NetworkMonitor networkMonitor; - // The list of NetworkRequests being satisfied by this Network. public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>(); public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>(); @@ -66,6 +65,10 @@ public class NetworkAgentInfo { networkMonitor = new NetworkMonitor(context, handler, this); } + public void addRequest(NetworkRequest networkRequest) { + networkRequests.put(networkRequest.requestId, networkRequest); + } + public String toString() { return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" + network + "} lp{" + diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 92b5f52..c93f85d 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -489,8 +489,10 @@ public class Tethering extends BaseNetworkObserver { } private class StateReceiver extends BroadcastReceiver { + @Override public void onReceive(Context content, Intent intent) { String action = intent.getAction(); + if (action == null) { return; } if (action.equals(UsbManager.ACTION_USB_STATE)) { synchronized (Tethering.this.mPublicSync) { boolean usbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index c7d2871..8fd55e7 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -230,7 +230,7 @@ public final class ContentService extends IContentService.Stub { // Notify for any user other than the caller's own requires permission. final int callingUserHandle = UserHandle.getCallingUserId(); if (userHandle != callingUserHandle) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, + mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, "no permission to notify other users"); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index d0b716d..5141d16 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -505,7 +505,7 @@ final class HdmiCecController { // Reply <Feature Abort> to initiator (source) for all requests. HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand( sourceAddress, message.getSource(), message.getOpcode(), - HdmiCecMessageBuilder.ABORT_REFUSED); + HdmiConstants.ABORT_REFUSED); sendCommand(cecMessage, null); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index 9a76734..6c2be34 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -26,15 +26,6 @@ import java.util.Arrays; * A helper class to build {@link HdmiCecMessage} from various cec commands. */ public class HdmiCecMessageBuilder { - // TODO: move these values to HdmiCec.java once make it internal constant class. - // CEC's ABORT reason values. - static final int ABORT_UNRECOGNIZED_MODE = 0; - static final int ABORT_NOT_IN_CORRECT_MODE = 1; - static final int ABORT_CANNOT_PROVIDE_SOURCE = 2; - static final int ABORT_INVALID_OPERAND = 3; - static final int ABORT_REFUSED = 4; - static final int ABORT_UNABLE_TO_DETERMINE = 5; - private static final int OSD_NAME_MAX_LENGTH = 13; private HdmiCecMessageBuilder() {} @@ -290,6 +281,64 @@ public class HdmiCecMessageBuilder { } /** + * Build <System Audio Mode Request> command. + * + * @param src source address of command + * @param avr destination address of command, it should be AVR + * @param avrPhysicalAddress physical address of AVR + * @param enableSystemAudio whether to enable System Audio Mode or not + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress, + boolean enableSystemAudio) { + if (enableSystemAudio) { + return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST, + physicalAddressToParam(avrPhysicalAddress)); + } else { + return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST); + } + } + + /** + * Build <Give Audio Status> command. + * + * @param src source address of command + * @param dest destination address of command + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildGiveAudioStatus(int src, int dest) { + return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_AUDIO_STATUS); + } + + /** + * Build <User Control Pressed> command. + * + * @param src source address of command + * @param dest destination address of command + * @param uiCommand keycode that user pressed + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) { + byte[] params = new byte[] { + (byte) uiCommand + }; + return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, params); + } + + /** + * Build <User Control Released> command. + * + * @param src source address of command + * @param dest destination address of command + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildUserControlReleased(int src, int dest) { + return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED); + } + + /***** Please ADD new buildXXX() methods above. ******/ + + /** * Build a {@link HdmiCecMessage} without extra parameter. * * @param src source address of command diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java new file mode 100644 index 0000000..a83d1ed --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +/** + * Defines constants related to HDMI-CEC protocol internal implementation. + * If a constant will be used in the public api, it should be located in + * {@link android.hardware.hdmi.HdmiCec}. + */ +final class HdmiConstants { + + // Constants related to operands of HDMI CEC commands. + // Refer to CEC Table 29 in HDMI Spec v1.4b. + // [Abort Reason] + static final int ABORT_UNRECOGNIZED_MODE = 0; + static final int ABORT_NOT_IN_CORRECT_MODE = 1; + static final int ABORT_CANNOT_PROVIDE_SOURCE = 2; + static final int ABORT_INVALID_OPERAND = 3; + static final int ABORT_REFUSED = 4; + static final int ABORT_UNABLE_TO_DETERMINE = 5; + + // [Audio Status] + static final int SYSTEM_AUDIO_STATUS_OFF = 0; + static final int SYSTEM_AUDIO_STATUS_ON = 1; + + // Constants related to UI Command Codes. + // Refer to CEC Table 30 in HDMI Spec v1.4b. + static final int UI_COMMAND_MUTE = 0x43; + static final int UI_COMMAND_MUTE_FUNCTION = 0x65; + static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66; + + private HdmiConstants() { /* cannot be instantiated */ } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index d775733..0f3fc21 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -118,8 +118,11 @@ public final class HdmiControlService extends SystemService { // TODO: it may need to hold lock if it's accessed from others. private boolean mArcStatusEnabled = false; + // Whether SystemAudioMode is "On" or not. + private boolean mSystemAudioMode; + // Handler running on service thread. It's used to run a task in service thread. - private Handler mHandler = new Handler(); + private final Handler mHandler = new Handler(); public HdmiControlService(Context context) { super(context); @@ -158,6 +161,9 @@ public final class HdmiControlService extends SystemService { } publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); + + // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and + // start to monitor the preference value and invoke SystemAudioActionFromTv if needed. } /** @@ -211,35 +217,43 @@ public final class HdmiControlService extends SystemService { * @param action {@link FeatureAction} to remove */ void removeAction(final FeatureAction action) { - runOnServiceThread(new Runnable() { - @Override - public void run() { - mActions.remove(action); - } - }); + assertRunOnServiceThread(); + mActions.remove(action); } // Remove all actions matched with the given Class type. private <T extends FeatureAction> void removeAction(final Class<T> clazz) { - runOnServiceThread(new Runnable() { - @Override - public void run() { - Iterator<FeatureAction> iter = mActions.iterator(); - while (iter.hasNext()) { - FeatureAction action = iter.next(); - if (action.getClass().equals(clazz)) { - action.clear(); - mActions.remove(action); - } - } + removeActionExcept(clazz, null); + } + + // Remove all actions matched with the given Class type besides |exception|. + <T extends FeatureAction> void removeActionExcept(final Class<T> clazz, + final FeatureAction exception) { + assertRunOnServiceThread(); + Iterator<FeatureAction> iter = mActions.iterator(); + while (iter.hasNext()) { + FeatureAction action = iter.next(); + if (action != exception && action.getClass().equals(clazz)) { + action.clear(); + mActions.remove(action); } - }); + } } private void runOnServiceThread(Runnable runnable) { mHandler.post(runnable); } + void runOnServiceThreadAtFrontOfQueue(Runnable runnable) { + mHandler.postAtFrontOfQueue(runnable); + } + + private void assertRunOnServiceThread() { + if (Looper.myLooper() != mHandler.getLooper()) { + throw new IllegalStateException("Should run on service thread."); + } + } + /** * Change ARC status into the given {@code enabled} status. * @@ -306,8 +320,12 @@ public final class HdmiControlService extends SystemService { case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS: handleReportPhysicalAddress(message); return true; - // TODO: Add remaining system information query such as - // <Give Device Power Status> and <Request Active Source> handler. + case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE: + handleSetSystemAudioMode(message); + return true; + case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS: + handleSystemAudioModeStatus(message); + return true; default: return dispatchMessageToAction(message); } @@ -413,7 +431,7 @@ public final class HdmiControlService extends SystemService { sendCecCommand( HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(), message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE, - HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE)); + HdmiConstants.ABORT_UNRECOGNIZED_MODE)); return; } @@ -438,6 +456,33 @@ public final class HdmiControlService extends SystemService { return false; } + private void handleSetSystemAudioMode(HdmiCecMessage message) { + if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) { + return; + } + SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this, + message.getDestination(), message.getSource(), + HdmiUtils.parseCommandParamSystemAudioStatus(message)); + addAndStartAction(action); + } + + private void handleSystemAudioModeStatus(HdmiCecMessage message) { + if (!isMessageForSystemAudio(message)) { + return; + } + setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message)); + } + + private boolean isMessageForSystemAudio(HdmiCecMessage message) { + if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM + || message.getDestination() != HdmiCec.ADDR_TV + || getAvrDeviceInfo() == null) { + Slog.w(TAG, "Skip abnormal CecMessage: " + message); + return false; + } + return true; + } + // Record class that monitors the event of the caller of being killed. Used to clean up // the listener list and record list accordingly. private final class HotplugEventListenerRecord implements IBinder.DeathRecipient { @@ -627,4 +672,32 @@ public final class HdmiControlService extends SystemService { Slog.e(TAG, "Invoking callback failed:" + e); } } + + HdmiCecDeviceInfo getAvrDeviceInfo() { + return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM); + } + + void setSystemAudioMode(boolean newMode) { + assertRunOnServiceThread(); + if (newMode != mSystemAudioMode) { + // TODO: Need to set the preference for SystemAudioMode. + // TODO: Need to handle the notification of changing the mode and + // to identify the notification should be handled in the service or TvSettings. + mSystemAudioMode = newMode; + } + } + + boolean getSystemAudioMode() { + assertRunOnServiceThread(); + return mSystemAudioMode; + } + + void setAudioStatus(boolean mute, int volume) { + // TODO: Hook up with AudioManager. + } + + boolean isInPresetInstallationMode() { + // TODO: Implement this. + return false; + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java new file mode 100644 index 0000000..ef128ed1 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; +import android.util.Slog; + +/** + * Various utilities to handle HDMI CEC messages. + */ +final class HdmiUtils { + + private HdmiUtils() { /* cannot be instantiated */ } + + /** + * Verify if the given address is for the given device type. If not it will throw + * {@link IllegalArgumentException}. + * + * @param logicalAddress the logical address to verify + * @param deviceType the device type to check + * @throw IllegalArgumentException + */ + static void verifyAddressType(int logicalAddress, int deviceType) { + int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); + if (actualDeviceType != deviceType) { + throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType + + ", Actual:" + actualDeviceType); + } + } + + /** + * Check if the given CEC message come from the given address. + * + * @param cmd the CEC message to check + * @param expectedAddress the expected source address of the given message + * @param tag the tag of caller module (for log message) + * @return true if the CEC message comes from the given address + */ + static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) { + int src = cmd.getSource(); + if (src != expectedAddress) { + Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]"); + return false; + } + return true; + } + + /** + * Parse the parameter block of CEC message as [System Audio Status]. + * + * @param cmd the CEC message to parse + * @return true if the given parameter has [ON] value + */ + static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) { + // TODO: Handle the exception when the length is wrong. + return cmd.getParams().length > 0 + && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON; + } +} diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java index 05614a4..08ca306 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java @@ -44,28 +44,15 @@ abstract class RequestArcAction extends FeatureAction { */ RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) { super(service, sourceAddress); - verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); - verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); + HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); + HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); mAvrAddress = avrAddress; } - private static void verifyAddressType(int logicalAddress, int deviceType) { - int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); - if (actualDeviceType != deviceType) { - throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType - + ", Actual:" + actualDeviceType); - } - } - @Override boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) { - return false; - } - - int src = cmd.getSource(); - if (src != mAvrAddress) { - Slog.w(TAG, "Invalid source [Expected:" + mAvrAddress + ", Actual:" + src + "]"); + if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE + || !HdmiUtils.checkCommandSource(cmd, mAvrAddress, TAG)) { return false; } int opcode = cmd.getOpcode(); diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java index e3525d8..d53d88d 100644 --- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java +++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java @@ -46,21 +46,12 @@ final class SetArcTransmissionStateAction extends FeatureAction { SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress, boolean enabled) { super(service, sourceAddress); - verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); - verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); + HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); + HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); mAvrAddress = avrAddress; mEnabled = enabled; } - // TODO: extract it as separate utility class. - private static void verifyAddressType(int logicalAddress, int deviceType) { - int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); - if (actualDeviceType != deviceType) { - throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType - + ", Actual:" + actualDeviceType); - } - } - @Override boolean start() { if (mEnabled) { diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java new file mode 100644 index 0000000..dde3342 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; + +/** + * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr. + */ +abstract class SystemAudioAction extends FeatureAction { + private static final String TAG = "SystemAudioAction"; + + // State in which waits for <SetSystemAudioMode>. + private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 1; + + // State in which waits for <ReportAudioStatus>. + private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 2; + + private static final int MAX_SEND_RETRY_COUNT = 2; + + private static final int ON_TIMEOUT_MS = 5000; + private static final int OFF_TIMEOUT_MS = TIMEOUT_MS; + + // Logical address of AV Receiver. + protected final int mAvrLogicalAddress; + + // The target audio status of the action, whether to enable the system audio mode or not. + protected boolean mTargetAudioStatus; + + private int mSendRetryCount = 0; + + /** + * Constructor + * + * @param service {@link HdmiControlService} instance + * @param sourceAddress logical address of source device (TV or STB). + * @param avrAddress logical address of AVR device + * @param targetStatus Whether to enable the system audio mode or not + * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid + */ + SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress, + boolean targetStatus) { + super(service, sourceAddress); + HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); + mAvrLogicalAddress = avrAddress; + mTargetAudioStatus = targetStatus; + } + + protected void sendSystemAudioModeRequest() { + int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress(); + HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress, + mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus); + sendCommand(command, new HdmiControlService.SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + if (error == HdmiControlService.SEND_RESULT_SUCCESS) { + mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE; + addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS); + } else { + setSystemAudioMode(false); + finish(); + } + } + }); + } + + private void handleSendSystemAudioModeRequestTimeout() { + if (!mTargetAudioStatus // Don't retry for Off case. + || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) { + setSystemAudioMode(false); + finish(); + return; + } + sendSystemAudioModeRequest(); + } + + protected void setSystemAudioMode(boolean mode) { + mService.setSystemAudioMode(mode); + } + + protected void sendGiveAudioStatus() { + HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress, + mAvrLogicalAddress); + sendCommand(command, new HdmiControlService.SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + if (error == HdmiControlService.SEND_RESULT_SUCCESS) { + mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS; + addTimer(mState, TIMEOUT_MS); + } else { + handleSendGiveAudioStatusFailure(); + } + } + }); + } + + private void handleSendGiveAudioStatusFailure() { + // TODO: Notify the failure status. + + int uiCommand = mService.getSystemAudioMode() + ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON + : HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF + sendUserControlPressedAndReleased(uiCommand); + finish(); + } + + private void sendUserControlPressedAndReleased(int uiCommand) { + sendCommand(HdmiCecMessageBuilder.buildUserControlPressed( + mSourceAddress, mAvrLogicalAddress, uiCommand)); + sendCommand(HdmiCecMessageBuilder.buildUserControlReleased( + mSourceAddress, mAvrLogicalAddress)); + } + + @Override + final boolean processCommand(HdmiCecMessage cmd) { + switch (mState) { + case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE: + // TODO: Handle <FeatureAbort> of <SystemAudioModeRequest> + if (cmd.getOpcode() != HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE + || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) { + return false; + } + boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd); + if (receivedStatus == mTargetAudioStatus) { + setSystemAudioMode(receivedStatus); + sendGiveAudioStatus(); + } else { + // Unexpected response, consider the request is newly initiated by AVR. + // To return 'false' will initiate new SystemAudioActionFromAvr by the control + // service. + finish(); + return false; + } + return true; + + case STATE_WAIT_FOR_REPORT_AUDIO_STATUS: + // TODO: Handle <FeatureAbort> of <GiveAudioStatus> + if (cmd.getOpcode() != HdmiCec.MESSAGE_REPORT_AUDIO_STATUS + || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) { + return false; + } + byte[] params = cmd.getParams(); + if (params.length > 0) { + boolean mute = (params[0] & 0x80) == 0x80; + int volume = params[0] & 0x7F; + mService.setAudioStatus(mute, volume); + if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) { + // Toggle AVR's mute status to match with the system audio status. + sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE); + } + } + finish(); + return true; + } + return false; + } + + protected void removeSystemAudioActionInProgress() { + mService.removeActionExcept(SystemAudioActionFromTv.class, this); + mService.removeActionExcept(SystemAudioActionFromAvr.class, this); + } + + @Override + final void handleTimerEvent(int state) { + if (mState != state) { + return; + } + switch (mState) { + case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE: + handleSendSystemAudioModeRequestTimeout(); + return; + case STATE_WAIT_FOR_REPORT_AUDIO_STATUS: + handleSendGiveAudioStatusFailure(); + return; + } + } +} diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java new file mode 100644 index 0000000..c5eb44b --- /dev/null +++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; + +/** + * Feature action that handles System Audio initiated by AVR devices. + */ +final class SystemAudioActionFromAvr extends SystemAudioAction { + /** + * Constructor + * + * @param service {@link HdmiControlService} instance + * @param tvAddress logical address of TV device + * @param avrAddress logical address of AVR device + * @param targetStatus Whether to enable the system audio mode or not + * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid + */ + SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress, + boolean targetStatus) { + super(service, tvAddress, avrAddress, targetStatus); + HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV); + } + + @Override + boolean start() { + removeSystemAudioActionInProgress(); + handleSystemAudioActionFromAvr(); + return true; + } + + private void handleSystemAudioActionFromAvr() { + if (mTargetAudioStatus == mService.getSystemAudioMode()) { + finish(); + return; + } + if (mService.isInPresetInstallationMode()) { + sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand( + mSourceAddress, mAvrLogicalAddress, + HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED)); + mTargetAudioStatus = false; + sendSystemAudioModeRequest(); + return; + } + // TODO: Stop the action for System Audio Mode initialization if it is running. + if (mTargetAudioStatus) { + setSystemAudioMode(true); + sendGiveAudioStatus(); + } else { + setSystemAudioMode(false); + finish(); + } + } +} diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java new file mode 100644 index 0000000..9994de6 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; + + +/** + * Feature action that handles System Audio initiated by TV devices. + */ +final class SystemAudioActionFromTv extends SystemAudioAction { + /** + * Constructor + * + * @param service {@link HdmiControlService} instance + * @param tvAddress logical address of TV device + * @param avrAddress logical address of AVR device + * @param targetStatus Whether to enable the system audio mode or not + * @throw IllegalArugmentException if device type of tvAddress is invalid + */ + SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress, + boolean targetStatus) { + super(service, tvAddress, avrAddress, targetStatus); + HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV); + } + + @Override + boolean start() { + // TODO: Check HDMI-CEC is enabled. + // TODO: Move to the waiting state if currently a routing change is in progress. + + removeSystemAudioActionInProgress(); + sendSystemAudioModeRequest(); + return true; + } +} diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index bab4895..5081bf7 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -44,7 +44,7 @@ import java.util.Map; * {@hide} */ public class NotificationUsageStats { - private static final boolean ENABLE_SQLITE_LOG = false; + private static final boolean ENABLE_SQLITE_LOG = true; // Guarded by synchronized(this). private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>(); diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java index 02f95e9..4ac2dcc 100644 --- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -34,6 +34,8 @@ import java.util.LinkedList; /** * This {@link NotificationSignalExtractor} attempts to validate * people references. Also elevates the priority of real people. + * + * {@hide} */ public class ValidateNotificationPeople implements NotificationSignalExtractor { private static final String TAG = "ValidateNotificationPeople"; @@ -147,7 +149,8 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { }; } - private String[] getExtraPeople(Bundle extras) { + // VisibleForTesting + public static String[] getExtraPeople(Bundle extras) { Object people = extras.get(Notification.EXTRA_PEOPLE); if (people instanceof String[]) { return (String[]) people; diff --git a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java index 85bde98..3d432dc 100644 --- a/services/core/java/com/android/server/pm/ForwardingIntentFilter.java +++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java @@ -26,44 +26,48 @@ import java.io.IOException; import android.os.UserHandle; /** - * The {@link PackageManagerService} maintains some {@link ForwardingIntentFilter}s for every user. - * If an {@link Intent} matches the {@link ForwardingIntentFilter}, then it can be forwarded to the - * {@link #mUserIdDest}. + * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user. + * If an {@link Intent} matches the {@link CrossProfileIntentFilter}, then activities in the user + * {@link #mTargetUserId} can access it. */ -class ForwardingIntentFilter extends IntentFilter { - private static final String ATTR_USER_ID_DEST = "userIdDest"; +class CrossProfileIntentFilter extends IntentFilter { + private static final String ATTR_TARGET_USER_ID = "targetUserId"; + private static final String ATTR_USER_ID_DEST = "userIdDest";//Old name. Kept for compatibility. private static final String ATTR_REMOVABLE = "removable"; private static final String ATTR_FILTER = "filter"; - private static final String TAG = "ForwardingIntentFilter"; + private static final String TAG = "CrossProfileIntentFilter"; // If the intent matches the IntentFilter, then it can be forwarded to this userId. - final int mUserIdDest; + final int mTargetUserId; boolean mRemovable; - ForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdDest) { + CrossProfileIntentFilter(IntentFilter filter, boolean removable, int targetUserId) { super(filter); - mUserIdDest = userIdDest; + mTargetUserId = targetUserId; mRemovable = removable; } - public int getUserIdDest() { - return mUserIdDest; + public int getTargetUserId() { + return mTargetUserId; } public boolean isRemovable() { return mRemovable; } - ForwardingIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException { - String userIdDestString = parser.getAttributeValue(null, ATTR_USER_ID_DEST); - if (userIdDestString == null) { - String msg = "Missing element under " + TAG +": " + ATTR_USER_ID_DEST + " at " + + CrossProfileIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException { + String targetUserIdString = parser.getAttributeValue(null, ATTR_TARGET_USER_ID); + if (targetUserIdString == null) { + targetUserIdString = parser.getAttributeValue(null, ATTR_USER_ID_DEST); + } + if (targetUserIdString == null) { + String msg = "Missing element under " + TAG +": " + ATTR_TARGET_USER_ID + " at " + parser.getPositionDescription(); PackageManagerService.reportSettingsProblem(Log.WARN, msg); - mUserIdDest = UserHandle.USER_NULL; + mTargetUserId = UserHandle.USER_NULL; } else { - mUserIdDest = Integer.parseInt(userIdDestString); + mTargetUserId = Integer.parseInt(targetUserIdString); } String removableString = parser.getAttributeValue(null, ATTR_REMOVABLE); if (removableString != null) { @@ -99,7 +103,7 @@ class ForwardingIntentFilter extends IntentFilter { } public void writeToXml(XmlSerializer serializer) throws IOException { - serializer.attribute(null, ATTR_USER_ID_DEST, Integer.toString(mUserIdDest)); + serializer.attribute(null, ATTR_TARGET_USER_ID, Integer.toString(mTargetUserId)); serializer.attribute(null, ATTR_REMOVABLE, Boolean.toString(mRemovable)); serializer.startTag(null, ATTR_FILTER); super.writeToXml(serializer); @@ -108,7 +112,7 @@ class ForwardingIntentFilter extends IntentFilter { @Override public String toString() { - return "ForwardingIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this)) - + " " + Integer.toString(mUserIdDest) + "}"; + return "CrossProfileIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this)) + + " " + Integer.toString(mTargetUserId) + "}"; } } diff --git a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java index 1616395..a335d3a 100644 --- a/services/core/java/com/android/server/pm/ForwardingIntentResolver.java +++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java @@ -23,22 +23,22 @@ import com.android.server.IntentResolver; import java.util.List; /** - * Used to find a list of {@link ForwardingIntentFilter}s that match an intent. + * Used to find a list of {@link CrossProfileIntentFilter}s that match an intent. */ -class ForwardingIntentResolver - extends IntentResolver<ForwardingIntentFilter, ForwardingIntentFilter> { +class CrossProfileIntentResolver + extends IntentResolver<CrossProfileIntentFilter, CrossProfileIntentFilter> { @Override - protected ForwardingIntentFilter[] newArray(int size) { - return new ForwardingIntentFilter[size]; + protected CrossProfileIntentFilter[] newArray(int size) { + return new CrossProfileIntentFilter[size]; } @Override - protected boolean isPackageForFilter(String packageName, ForwardingIntentFilter filter) { + protected boolean isPackageForFilter(String packageName, CrossProfileIntentFilter filter) { return false; } @Override - protected void sortResults(List<ForwardingIntentFilter> results) { + protected void sortResults(List<CrossProfileIntentFilter> results) { //We don't sort the results } } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 5e3325c..25ebfc0 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -40,6 +40,7 @@ import android.util.Log; import android.util.Slog; import com.android.internal.content.PackageMonitor; +import com.android.server.SystemService; import java.util.ArrayList; import java.util.List; @@ -48,358 +49,374 @@ import java.util.List; * Service that manages requests and callbacks for launchers that support * managed profiles. */ -public class LauncherAppsService extends ILauncherApps.Stub { - private static final boolean DEBUG = false; - private static final String TAG = "LauncherAppsService"; - private final Context mContext; - private final PackageManager mPm; - private final UserManager mUm; - private final PackageCallbackList<IOnAppsChangedListener> mListeners - = new PackageCallbackList<IOnAppsChangedListener>(); - private MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); +public class LauncherAppsService extends SystemService { + + private final LauncherAppsImpl mLauncherAppsImpl; public LauncherAppsService(Context context) { - mContext = context; - mPm = mContext.getPackageManager(); - mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + super(context); + mLauncherAppsImpl = new LauncherAppsImpl(context); } - /* - * @see android.content.pm.ILauncherApps#addOnAppsChangedListener( - * android.content.pm.IOnAppsChangedListener) - */ @Override - public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException { - synchronized (mListeners) { - if (DEBUG) { - Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle()); - } - if (mListeners.getRegisteredCallbackCount() == 0) { + public void onStart() { + publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl); + } + + class LauncherAppsImpl extends ILauncherApps.Stub { + private static final boolean DEBUG = false; + private static final String TAG = "LauncherAppsService"; + private final Context mContext; + private final PackageManager mPm; + private final UserManager mUm; + private final PackageCallbackList<IOnAppsChangedListener> mListeners + = new PackageCallbackList<IOnAppsChangedListener>(); + + private MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); + + public LauncherAppsImpl(Context context) { + mContext = context; + mPm = mContext.getPackageManager(); + mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + } + + /* + * @see android.content.pm.ILauncherApps#addOnAppsChangedListener( + * android.content.pm.IOnAppsChangedListener) + */ + @Override + public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException { + synchronized (mListeners) { if (DEBUG) { - Log.d(TAG, "Starting package monitoring"); + Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle()); } - startWatchingPackageBroadcasts(); + if (mListeners.getRegisteredCallbackCount() == 0) { + if (DEBUG) { + Log.d(TAG, "Starting package monitoring"); + } + startWatchingPackageBroadcasts(); + } + mListeners.unregister(listener); + mListeners.register(listener, Binder.getCallingUserHandle()); } - mListeners.unregister(listener); - mListeners.register(listener, Binder.getCallingUserHandle()); } - } - /* - * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener( - * android.content.pm.IOnAppsChangedListener) - */ - @Override - public void removeOnAppsChangedListener(IOnAppsChangedListener listener) - throws RemoteException { - synchronized (mListeners) { - if (DEBUG) { - Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle()); - } - mListeners.unregister(listener); - if (mListeners.getRegisteredCallbackCount() == 0) { - stopWatchingPackageBroadcasts(); + /* + * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener( + * android.content.pm.IOnAppsChangedListener) + */ + @Override + public void removeOnAppsChangedListener(IOnAppsChangedListener listener) + throws RemoteException { + synchronized (mListeners) { + if (DEBUG) { + Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle()); + } + mListeners.unregister(listener); + if (mListeners.getRegisteredCallbackCount() == 0) { + stopWatchingPackageBroadcasts(); + } } } - } - - /** - * Register a receiver to watch for package broadcasts - */ - private void startWatchingPackageBroadcasts() { - mPackageMonitor.register(mContext, null, UserHandle.ALL, true); - } - /** - * Unregister package broadcast receiver - */ - private void stopWatchingPackageBroadcasts() { - if (DEBUG) { - Log.d(TAG, "Stopped watching for packages"); + /** + * Register a receiver to watch for package broadcasts + */ + private void startWatchingPackageBroadcasts() { + mPackageMonitor.register(mContext, null, UserHandle.ALL, true); } - mPackageMonitor.unregister(); - } - void checkCallbackCount() { - synchronized (mListeners) { + /** + * Unregister package broadcast receiver + */ + private void stopWatchingPackageBroadcasts() { if (DEBUG) { - Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount()); - } - if (mListeners.getRegisteredCallbackCount() == 0) { - stopWatchingPackageBroadcasts(); + Log.d(TAG, "Stopped watching for packages"); } + mPackageMonitor.unregister(); } - } - /** - * Checks if the caller is in the same group as the userToCheck. - */ - private void ensureInUserProfiles(UserHandle userToCheck, String message) { - final int callingUserId = UserHandle.getCallingUserId(); - final int targetUserId = userToCheck.getIdentifier(); - - if (targetUserId == callingUserId) return; - - long ident = Binder.clearCallingIdentity(); - try { - UserInfo callingUserInfo = mUm.getUserInfo(callingUserId); - UserInfo targetUserInfo = mUm.getUserInfo(targetUserId); - if (targetUserInfo == null - || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID - || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) { - throw new SecurityException(message); + void checkCallbackCount() { + synchronized (mListeners) { + if (DEBUG) { + Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount()); + } + if (mListeners.getRegisteredCallbackCount() == 0) { + stopWatchingPackageBroadcasts(); + } } - } finally { - Binder.restoreCallingIdentity(ident); } - } - /** - * Checks if the user is enabled. - */ - private boolean isUserEnabled(UserHandle user) { - long ident = Binder.clearCallingIdentity(); - try { - UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier()); - return targetUserInfo != null && targetUserInfo.isEnabled(); - } finally { - Binder.restoreCallingIdentity(ident); - } - } + /** + * Checks if the caller is in the same group as the userToCheck. + */ + private void ensureInUserProfiles(UserHandle userToCheck, String message) { + final int callingUserId = UserHandle.getCallingUserId(); + final int targetUserId = userToCheck.getIdentifier(); - @Override - public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user) - throws RemoteException { - ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user); - if (!isUserEnabled(user)) { - return new ArrayList<ResolveInfo>(); - } + if (targetUserId == callingUserId) return; - final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); - mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); - mainIntent.setPackage(packageName); - long ident = Binder.clearCallingIdentity(); - try { - List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0, - user.getIdentifier()); - return apps; - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public ResolveInfo resolveActivity(Intent intent, UserHandle user) - throws RemoteException { - ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user); - if (!isUserEnabled(user)) { - return null; + long ident = Binder.clearCallingIdentity(); + try { + UserInfo callingUserInfo = mUm.getUserInfo(callingUserId); + UserInfo targetUserInfo = mUm.getUserInfo(targetUserId); + if (targetUserInfo == null + || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID + || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) { + throw new SecurityException(message); + } + } finally { + Binder.restoreCallingIdentity(ident); + } } - long ident = Binder.clearCallingIdentity(); - try { - ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier()); - return app; - } finally { - Binder.restoreCallingIdentity(ident); + /** + * Checks if the user is enabled. + */ + private boolean isUserEnabled(UserHandle user) { + long ident = Binder.clearCallingIdentity(); + try { + UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier()); + return targetUserInfo != null && targetUserInfo.isEnabled(); + } finally { + Binder.restoreCallingIdentity(ident); + } } - } - @Override - public boolean isPackageEnabled(String packageName, UserHandle user) - throws RemoteException { - ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user); - if (!isUserEnabled(user)) { - return false; - } + @Override + public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user) + throws RemoteException { + ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user); + if (!isUserEnabled(user)) { + return new ArrayList<ResolveInfo>(); + } - long ident = Binder.clearCallingIdentity(); - try { - IPackageManager pm = AppGlobals.getPackageManager(); - PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier()); - return info != null && info.applicationInfo.enabled; - } finally { - Binder.restoreCallingIdentity(ident); + final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + mainIntent.setPackage(packageName); + long ident = Binder.clearCallingIdentity(); + try { + List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0, + user.getIdentifier()); + return apps; + } finally { + Binder.restoreCallingIdentity(ident); + } } - } - @Override - public boolean isActivityEnabled(ComponentName component, UserHandle user) - throws RemoteException { - ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user); - if (!isUserEnabled(user)) { - return false; - } + @Override + public ResolveInfo resolveActivity(Intent intent, UserHandle user) + throws RemoteException { + ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user); + if (!isUserEnabled(user)) { + return null; + } - long ident = Binder.clearCallingIdentity(); - try { - IPackageManager pm = AppGlobals.getPackageManager(); - ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier()); - return info != null && info.isEnabled(); - } finally { - Binder.restoreCallingIdentity(ident); + long ident = Binder.clearCallingIdentity(); + try { + ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier()); + return app; + } finally { + Binder.restoreCallingIdentity(ident); + } } - } - @Override - public void startActivityAsUser(ComponentName component, Rect sourceBounds, - Bundle opts, UserHandle user) throws RemoteException { - ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user); - if (!isUserEnabled(user)) { - throw new IllegalStateException("Cannot start activity for disabled profile " + user); - } + @Override + public boolean isPackageEnabled(String packageName, UserHandle user) + throws RemoteException { + ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user); + if (!isUserEnabled(user)) { + return false; + } - Intent launchIntent = new Intent(Intent.ACTION_MAIN); - launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); - launchIntent.setComponent(component); - launchIntent.setSourceBounds(sourceBounds); - launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - long ident = Binder.clearCallingIdentity(); - try { - mContext.startActivityAsUser(launchIntent, opts, user); - } finally { - Binder.restoreCallingIdentity(ident); + long ident = Binder.clearCallingIdentity(); + try { + IPackageManager pm = AppGlobals.getPackageManager(); + PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier()); + return info != null && info.applicationInfo.enabled; + } finally { + Binder.restoreCallingIdentity(ident); + } } - } - private class MyPackageMonitor extends PackageMonitor { - - /** Checks if user is a profile of or same as listeningUser. - * and the user is enabled. */ - private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser, - String debugMsg) { - if (user.getIdentifier() == listeningUser.getIdentifier()) { - if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg); - return true; + @Override + public boolean isActivityEnabled(ComponentName component, UserHandle user) + throws RemoteException { + ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user); + if (!isUserEnabled(user)) { + return false; } + long ident = Binder.clearCallingIdentity(); try { - UserInfo userInfo = mUm.getUserInfo(user.getIdentifier()); - UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier()); - if (userInfo == null || listeningUserInfo == null - || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID - || userInfo.profileGroupId != listeningUserInfo.profileGroupId - || !userInfo.isEnabled()) { - if (DEBUG) { - Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":" - + debugMsg); - } - return false; - } else { - if (DEBUG) { - Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":" - + debugMsg); - } - return true; - } + IPackageManager pm = AppGlobals.getPackageManager(); + ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier()); + return info != null && info.isEnabled(); } finally { Binder.restoreCallingIdentity(ident); } } @Override - public void onPackageAdded(String packageName, int uid) { - UserHandle user = new UserHandle(getChangingUserId()); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); - UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue; - try { - listener.onPackageAdded(user, packageName); - } catch (RemoteException re) { - Slog.d(TAG, "Callback failed ", re); - } + public void startActivityAsUser(ComponentName component, Rect sourceBounds, + Bundle opts, UserHandle user) throws RemoteException { + ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user); + if (!isUserEnabled(user)) { + throw new IllegalStateException("Cannot start activity for disabled profile " + user); } - mListeners.finishBroadcast(); - super.onPackageAdded(packageName, uid); + Intent launchIntent = new Intent(Intent.ACTION_MAIN); + launchIntent.addCategory(Intent.CATEGORY_LAUNCHER); + launchIntent.setComponent(component); + launchIntent.setSourceBounds(sourceBounds); + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + long ident = Binder.clearCallingIdentity(); + try { + mContext.startActivityAsUser(launchIntent, opts, user); + } finally { + Binder.restoreCallingIdentity(ident); + } } - @Override - public void onPackageRemoved(String packageName, int uid) { - UserHandle user = new UserHandle(getChangingUserId()); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); - UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue; + private class MyPackageMonitor extends PackageMonitor { + + /** Checks if user is a profile of or same as listeningUser. + * and the user is enabled. */ + private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser, + String debugMsg) { + if (user.getIdentifier() == listeningUser.getIdentifier()) { + if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg); + return true; + } + long ident = Binder.clearCallingIdentity(); try { - listener.onPackageRemoved(user, packageName); - } catch (RemoteException re) { - Slog.d(TAG, "Callback failed ", re); + UserInfo userInfo = mUm.getUserInfo(user.getIdentifier()); + UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier()); + if (userInfo == null || listeningUserInfo == null + || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID + || userInfo.profileGroupId != listeningUserInfo.profileGroupId + || !userInfo.isEnabled()) { + if (DEBUG) { + Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":" + + debugMsg); + } + return false; + } else { + if (DEBUG) { + Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":" + + debugMsg); + } + return true; + } + } finally { + Binder.restoreCallingIdentity(ident); } } - mListeners.finishBroadcast(); - super.onPackageRemoved(packageName, uid); - } - - @Override - public void onPackageModified(String packageName) { - UserHandle user = new UserHandle(getChangingUserId()); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); - UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue; - try { - listener.onPackageChanged(user, packageName); - } catch (RemoteException re) { - Slog.d(TAG, "Callback failed ", re); + @Override + public void onPackageAdded(String packageName, int uid) { + UserHandle user = new UserHandle(getChangingUserId()); + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue; + try { + listener.onPackageAdded(user, packageName); + } catch (RemoteException re) { + Slog.d(TAG, "Callback failed ", re); + } } + mListeners.finishBroadcast(); + + super.onPackageAdded(packageName, uid); } - mListeners.finishBroadcast(); - super.onPackageModified(packageName); - } + @Override + public void onPackageRemoved(String packageName, int uid) { + UserHandle user = new UserHandle(getChangingUserId()); + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue; + try { + listener.onPackageRemoved(user, packageName); + } catch (RemoteException re) { + Slog.d(TAG, "Callback failed ", re); + } + } + mListeners.finishBroadcast(); - @Override - public void onPackagesAvailable(String[] packages) { - UserHandle user = new UserHandle(getChangingUserId()); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); - UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue; - try { - listener.onPackagesAvailable(user, packages, isReplacing()); - } catch (RemoteException re) { - Slog.d(TAG, "Callback failed ", re); + super.onPackageRemoved(packageName, uid); + } + + @Override + public void onPackageModified(String packageName) { + UserHandle user = new UserHandle(getChangingUserId()); + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue; + try { + listener.onPackageChanged(user, packageName); + } catch (RemoteException re) { + Slog.d(TAG, "Callback failed ", re); + } } + mListeners.finishBroadcast(); + + super.onPackageModified(packageName); } - mListeners.finishBroadcast(); - super.onPackagesAvailable(packages); - } + @Override + public void onPackagesAvailable(String[] packages) { + UserHandle user = new UserHandle(getChangingUserId()); + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue; + try { + listener.onPackagesAvailable(user, packages, isReplacing()); + } catch (RemoteException re) { + Slog.d(TAG, "Callback failed ", re); + } + } + mListeners.finishBroadcast(); - @Override - public void onPackagesUnavailable(String[] packages) { - UserHandle user = new UserHandle(getChangingUserId()); - final int n = mListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); - UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue; - try { - listener.onPackagesUnavailable(user, packages, isReplacing()); - } catch (RemoteException re) { - Slog.d(TAG, "Callback failed ", re); + super.onPackagesAvailable(packages); + } + + @Override + public void onPackagesUnavailable(String[] packages) { + UserHandle user = new UserHandle(getChangingUserId()); + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); + UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); + if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue; + try { + listener.onPackagesUnavailable(user, packages, isReplacing()); + } catch (RemoteException re) { + Slog.d(TAG, "Callback failed ", re); + } } + mListeners.finishBroadcast(); + + super.onPackagesUnavailable(packages); } - mListeners.finishBroadcast(); - super.onPackagesUnavailable(packages); } - } - - class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> { - @Override - public void onCallbackDied(T callback, Object cookie) { - checkCallbackCount(); + class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> { + @Override + public void onCallbackDied(T callback, Object cookie) { + checkCallbackCount(); + } } } -} +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index f90d7ab..3ed73f7 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -32,6 +32,7 @@ import android.content.pm.PackageParser.PackageLite; import android.content.pm.Signature; import android.os.Build; import android.os.Bundle; +import android.os.FileBridge; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; @@ -114,7 +115,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private boolean mPermissionsConfirmed; private boolean mInvalid; - private ArrayList<WritePipe> mPipes = new ArrayList<>(); + private ArrayList<FileBridge> mBridges = new ArrayList<>(); private IPackageInstallObserver2 mRemoteObserver; @@ -159,14 +160,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Quick sanity check of state, and allocate a pipe for ourselves. We // then do heavy disk allocation outside the lock, but this open pipe // will block any attempted install transitions. - final WritePipe pipe; + final FileBridge bridge; synchronized (mLock) { if (!mMutationsAllowed) { throw new IllegalStateException("Mutations not allowed"); } - pipe = new WritePipe(); - mPipes.add(pipe); + bridge = new FileBridge(); + mBridges.add(bridge); } try { @@ -194,9 +195,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); } - pipe.setTargetFd(targetFd); - pipe.start(); - return pipe.getWriteFd(); + bridge.setTargetFile(targetFd); + bridge.start(); + return new ParcelFileDescriptor(bridge.getClientSocket()); } catch (ErrnoException e) { throw new IllegalStateException("Failed to write", e); @@ -218,8 +219,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Verify that all writers are hands-off if (mMutationsAllowed) { - for (WritePipe pipe : mPipes) { - if (!pipe.isClosed()) { + for (FileBridge bridge : mBridges) { + if (!bridge.isClosed()) { throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED, "Files still open"); } @@ -482,52 +483,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - private static class WritePipe extends Thread { - private final ParcelFileDescriptor[] mPipe; - - private FileDescriptor mTargetFd; - - private volatile boolean mClosed; - - public WritePipe() { - try { - mPipe = ParcelFileDescriptor.createPipe(); - } catch (IOException e) { - throw new IllegalStateException("Failed to create pipe"); - } - } - - public boolean isClosed() { - return mClosed; - } - - public void setTargetFd(FileDescriptor targetFd) { - mTargetFd = targetFd; - } - - public ParcelFileDescriptor getWriteFd() { - return mPipe[1]; - } - - @Override - public void run() { - FileInputStream in = null; - FileOutputStream out = null; - try { - // TODO: look at switching to sendfile(2) to speed up - in = new FileInputStream(mPipe[0].getFileDescriptor()); - out = new FileOutputStream(mTargetFd); - Streams.copy(in, out); - } catch (IOException e) { - Slog.w(TAG, "Failed to stream data: " + e); - } finally { - IoUtils.closeQuietly(mPipe[0]); - IoUtils.closeQuietly(mTargetFd); - mClosed = true; - } - } - } - private class InstallFailedException extends Exception { private final int error; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d505e81..bb93663 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -40,6 +40,7 @@ import com.android.internal.R; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.content.NativeLibraryHelper.ApkHandle; import com.android.internal.content.PackageHelper; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; @@ -3374,25 +3375,26 @@ public class PackageManagerService extends IPackageManager.Stub { * Returns if intent can be forwarded from the userId from to dest */ @Override - public boolean canForwardTo(Intent intent, String resolvedType, int userIdFrom, int userIdDest) { + public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId, + int targetUserId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - List<ForwardingIntentFilter> matches = - getMatchingForwardingIntentFilters(intent, resolvedType, userIdFrom); + List<CrossProfileIntentFilter> matches = + getMatchingCrossProfileIntentFilters(intent, resolvedType, sourceUserId); if (matches != null) { int size = matches.size(); for (int i = 0; i < size; i++) { - if (matches.get(i).getUserIdDest() == userIdDest) return true; + if (matches.get(i).getTargetUserId() == targetUserId) return true; } } return false; } - private List<ForwardingIntentFilter> getMatchingForwardingIntentFilters(Intent intent, + private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { - ForwardingIntentResolver fir = mSettings.mForwardingIntentResolvers.get(userId); - if (fir != null) { - return fir.queryIntent(intent, resolvedType, false, userId); + CrossProfileIntentResolver cpir = mSettings.mCrossProfileIntentResolvers.get(userId); + if (cpir != null) { + return cpir.queryIntent(intent, resolvedType, false, userId); } return null; } @@ -3428,31 +3430,31 @@ public class PackageManagerService extends IPackageManager.Stub { List<ResolveInfo> result = mActivities.queryIntent(intent, resolvedType, flags, userId); // Checking if we can forward the intent to another user - List<ForwardingIntentFilter> fifs = - getMatchingForwardingIntentFilters(intent, resolvedType, userId); - if (fifs != null) { - ForwardingIntentFilter forwardingIntentFilterWithResult = null; + List<CrossProfileIntentFilter> cpifs = + getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); + if (cpifs != null) { + CrossProfileIntentFilter crossProfileIntentFilterWithResult = null; HashSet<Integer> alreadyTriedUserIds = new HashSet<Integer>(); - for (ForwardingIntentFilter fif : fifs) { - int userIdDest = fif.getUserIdDest(); - // Two {@link ForwardingIntentFilter}s can have the same userIdDest and + for (CrossProfileIntentFilter cpif : cpifs) { + int targetUserId = cpif.getTargetUserId(); + // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and // match the same an intent. For performance reasons, it is better not to // run queryIntent twice for the same userId - if (!alreadyTriedUserIds.contains(userIdDest)) { + if (!alreadyTriedUserIds.contains(targetUserId)) { List<ResolveInfo> resultUser = mActivities.queryIntent(intent, - resolvedType, flags, userIdDest); + resolvedType, flags, targetUserId); if (resultUser != null) { - forwardingIntentFilterWithResult = fif; + crossProfileIntentFilterWithResult = cpif; // As soon as there is a match in another user, we add the // intentForwarderActivity to the list of ResolveInfo. break; } - alreadyTriedUserIds.add(userIdDest); + alreadyTriedUserIds.add(targetUserId); } } - if (forwardingIntentFilterWithResult != null) { + if (crossProfileIntentFilterWithResult != null) { ResolveInfo forwardingResolveInfo = createForwardingResolveInfo( - forwardingIntentFilterWithResult, userId); + crossProfileIntentFilterWithResult, userId); result.add(forwardingResolveInfo); } } @@ -3467,10 +3469,11 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private ResolveInfo createForwardingResolveInfo(ForwardingIntentFilter fif, int userIdFrom) { + private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter cpif, + int sourceUserId) { String className; - int userIdDest = fif.getUserIdDest(); - if (userIdDest == UserHandle.USER_OWNER) { + int targetUserId = cpif.getTargetUserId(); + if (targetUserId == UserHandle.USER_OWNER) { className = FORWARD_INTENT_TO_USER_OWNER; } else { className = FORWARD_INTENT_TO_MANAGED_PROFILE; @@ -3478,14 +3481,14 @@ public class PackageManagerService extends IPackageManager.Stub { ComponentName forwardingActivityComponentName = new ComponentName( mAndroidApplication.packageName, className); ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0, - userIdFrom); + sourceUserId); ResolveInfo forwardingResolveInfo = new ResolveInfo(); forwardingResolveInfo.activityInfo = forwardingActivityInfo; forwardingResolveInfo.priority = 0; forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; - forwardingResolveInfo.filter = fif; + forwardingResolveInfo.filter = cpif; return forwardingResolveInfo; } @@ -4146,7 +4149,7 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } PackageParser.Package pkg = scanPackageLI(file, - flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null); + flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null, null); // Don't mess around with apps in system partition. if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { @@ -4213,7 +4216,7 @@ public class PackageManagerService extends IPackageManager.Stub { * Returns null in case of errors and the error code is stored in mLastScanError */ private PackageParser.Package scanPackageLI(File scanFile, - int parseFlags, int scanMode, long currentTime, UserHandle user) { + int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) { mLastScanError = PackageManager.INSTALL_SUCCEEDED; String scanPath = scanFile.getPath(); if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath); @@ -4281,7 +4284,7 @@ public class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; return null; } else { - // The current app on the system partion is better than + // The current app on the system partition is better than // what we have updated to on the data partition; switch // back to the system partition version. // At this point, its safely assumed that package installation for @@ -4400,7 +4403,7 @@ public class PackageManagerService extends IPackageManager.Stub { setApplicationInfoPaths(pkg, codePath, resPath); // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode - | SCAN_UPDATE_SIGNATURE, currentTime, user); + | SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride); /* * If the system app should be overridden by a previously installed @@ -4943,7 +4946,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private PackageParser.Package scanPackageLI(PackageParser.Package pkg, - int parseFlags, int scanMode, long currentTime, UserHandle user) { + int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) { File scanFile = new File(pkg.mScanPath); if (scanFile == null || pkg.applicationInfo.sourceDir == null || pkg.applicationInfo.publicSourceDir == null) { @@ -5393,7 +5396,22 @@ public class PackageManagerService extends IPackageManager.Stub { * only for non-system apps and system app upgrades. */ if (pkg.applicationInfo.nativeLibraryDir != null) { + final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); try { + // Enable gross and lame hacks for apps that are built with old + // SDK tools. We must scan their APKs for renderscript bitcode and + // not launch them if it's present. Don't bother checking on devices + // that don't have 64 bit support. + String[] abiList = Build.SUPPORTED_ABIS; + boolean hasLegacyRenderscriptBitcode = false; + if (abiOverride != null) { + abiList = new String[] { abiOverride }; + } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + hasLegacyRenderscriptBitcode = true; + } + File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); final String dataPathString = dataPath.getCanonicalPath(); @@ -5409,21 +5427,26 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "removed obsolete native libraries for system package " + path); } - - setInternalAppAbi(pkg, pkgSetting); + if (abiOverride != null || hasLegacyRenderscriptBitcode) { + pkg.applicationInfo.cpuAbi = abiList[0]; + pkgSetting.cpuAbiString = abiList[0]; + } else { + setInternalAppAbi(pkg, pkgSetting); + } } else { if (!isForwardLocked(pkg) && !isExternal(pkg)) { /* - * Update native library dir if it starts with - * /data/data - */ + * Update native library dir if it starts with + * /data/data + */ if (nativeLibraryDir.getPath().startsWith(dataPathString)) { setInternalAppNativeLibraryPath(pkg, pkgSetting); nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); } try { - int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir); + int copyRet = copyNativeLibrariesForInternalApp(handle, + nativeLibraryDir, abiList); if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { Slog.e(TAG, "Unable to copy native libraries"); mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; @@ -5433,7 +5456,9 @@ public class PackageManagerService extends IPackageManager.Stub { // We've successfully copied native libraries across, so we make a // note of what ABI we're using if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) { - pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[copyRet]; + pkg.applicationInfo.cpuAbi = abiList[copyRet]; + } else if (abiOverride != null || hasLegacyRenderscriptBitcode) { + pkg.applicationInfo.cpuAbi = abiList[0]; } else { pkg.applicationInfo.cpuAbi = null; } @@ -5450,20 +5475,22 @@ public class PackageManagerService extends IPackageManager.Stub { // to clean this up but we'll need to change the interface between this service // and IMediaContainerService (but doing so will spread this logic out, rather // than centralizing it). - final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); - final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); if (abi >= 0) { - pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[abi]; + pkg.applicationInfo.cpuAbi = abiList[abi]; } else if (abi == PackageManager.NO_NATIVE_LIBRARIES) { // Note that (non upgraded) system apps will not have any native // libraries bundled in their APK, but we're guaranteed not to be // such an app at this point. - pkg.applicationInfo.cpuAbi = null; + if (abiOverride != null || hasLegacyRenderscriptBitcode) { + pkg.applicationInfo.cpuAbi = abiList[0]; + } else { + pkg.applicationInfo.cpuAbi = null; + } } else { mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; } - handle.close(); } if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path); @@ -5480,8 +5507,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + + pkgSetting.cpuAbiString = pkg.applicationInfo.cpuAbi; } catch (IOException ioe) { Slog.e(TAG, "Unable to get canonical file " + ioe.toString()); + } finally { + handle.close(); } } pkg.mScanPath = path; @@ -6173,8 +6204,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir) - throws IOException { + private static int copyNativeLibrariesForInternalApp(ApkHandle handle, + final File nativeLibraryDir, String[] abiList) throws IOException { if (!nativeLibraryDir.isDirectory()) { nativeLibraryDir.delete(); @@ -6196,21 +6227,16 @@ public class PackageManagerService extends IPackageManager.Stub { * If this is an internal application or our nativeLibraryPath points to * the app-lib directory, unpack the libraries if necessary. */ - final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); - try { - int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); - if (abi >= 0) { - int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - nativeLibraryDir, Build.SUPPORTED_ABIS[abi]); - if (copyRet != PackageManager.INSTALL_SUCCEEDED) { - return copyRet; - } + int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); + if (abi >= 0) { + int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, + nativeLibraryDir, Build.SUPPORTED_ABIS[abi]); + if (copyRet != PackageManager.INSTALL_SUCCEEDED) { + return copyRet; } - - return abi; - } finally { - handle.close(); } + + return abi; } private void killApplication(String pkgName, int appId, String reason) { @@ -7534,7 +7560,7 @@ public class PackageManagerService extends IPackageManager.Stub { } p = scanPackageLI(fullPath, flags, SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME, - System.currentTimeMillis(), UserHandle.ALL); + System.currentTimeMillis(), UserHandle.ALL, null); if (p != null) { /* * TODO this seems dangerous as the package may have @@ -7655,6 +7681,16 @@ public class PackageManagerService extends IPackageManager.Stub { if (observer == null && observer2 == null) { throw new IllegalArgumentException("No install observer supplied"); } + installPackageWithVerificationEncryptionAndAbiOverrideEtc(packageURI, observer, observer2, + flags, installerPackageName, verificationParams, encryptionParams, null); + } + + @Override + public void installPackageWithVerificationEncryptionAndAbiOverrideEtc(Uri packageURI, + IPackageInstallObserver observer, IPackageInstallObserver2 observer2, + int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams, + String packageAbiOverride) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); @@ -7694,7 +7730,8 @@ public class PackageManagerService extends IPackageManager.Stub { final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(packageURI, observer, observer2, filteredFlags, - installerPackageName, verificationParams, encryptionParams, user); + installerPackageName, verificationParams, encryptionParams, user, + packageAbiOverride); mHandler.sendMessage(msg); } @@ -8403,11 +8440,14 @@ public class PackageManagerService extends IPackageManager.Stub { private int mRet; private File mTempPackage; final ContainerEncryptionParams encryptionParams; + final String packageAbiOverride; + final String packageInstructionSetOverride; InstallParams(Uri packageURI, IPackageInstallObserver observer, IPackageInstallObserver2 observer2, int flags, String installerPackageName, VerificationParams verificationParams, - ContainerEncryptionParams encryptionParams, UserHandle user) { + ContainerEncryptionParams encryptionParams, UserHandle user, + String packageAbiOverride) { super(user); this.mPackageURI = packageURI; this.flags = flags; @@ -8416,6 +8456,9 @@ public class PackageManagerService extends IPackageManager.Stub { this.installerPackageName = installerPackageName; this.verificationParams = verificationParams; this.encryptionParams = encryptionParams; + this.packageAbiOverride = packageAbiOverride; + this.packageInstructionSetOverride = (packageAbiOverride == null) ? + packageAbiOverride : VMRuntime.getInstructionSet(packageAbiOverride); } @Override @@ -8561,7 +8604,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Remote call to find out default install location final String packageFilePath = packageFile.getAbsolutePath(); pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags, - lowThreshold); + lowThreshold, packageAbiOverride); /* * If we have too little free space, try to free cache @@ -8570,10 +8613,10 @@ public class PackageManagerService extends IPackageManager.Stub { if (pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { final long size = mContainerService.calculateInstalledSize( - packageFilePath, isForwardLocked()); + packageFilePath, isForwardLocked(), packageAbiOverride); if (mInstaller.freeCache(size + lowThreshold) >= 0) { pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, - flags, lowThreshold); + flags, lowThreshold, packageAbiOverride); } /* * The cache free must have deleted the file we @@ -8993,11 +9036,12 @@ public class PackageManagerService extends IPackageManager.Stub { final ManifestDigest manifestDigest; final UserHandle user; final String instructionSet; + final String abiOverride; InstallArgs(Uri packageURI, IPackageInstallObserver observer, IPackageInstallObserver2 observer2, int flags, String installerPackageName, ManifestDigest manifestDigest, - UserHandle user, String instructionSet) { + UserHandle user, String instructionSet, String abiOverride) { this.packageURI = packageURI; this.flags = flags; this.observer = observer; @@ -9006,6 +9050,7 @@ public class PackageManagerService extends IPackageManager.Stub { this.manifestDigest = manifestDigest; this.user = user; this.instructionSet = instructionSet; + this.abiOverride = abiOverride; } abstract void createCopyFile(); @@ -9061,12 +9106,13 @@ public class PackageManagerService extends IPackageManager.Stub { FileInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.observer2, params.flags, params.installerPackageName, params.getManifestDigest(), - params.getUser(), null /* instruction set */); + params.getUser(), params.packageInstructionSetOverride, + params.packageAbiOverride); } FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, String instructionSet) { - super(null, null, null, 0, null, null, null, instructionSet); + super(null, null, null, 0, null, null, null, instructionSet, null); File codeFile = new File(fullCodePath); installDir = codeFile.getParentFile(); codeFileName = fullCodePath; @@ -9075,7 +9121,7 @@ public class PackageManagerService extends IPackageManager.Stub { } FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) { - super(packageURI, null, null, 0, null, null, null, instructionSet); + super(packageURI, null, null, 0, null, null, null, instructionSet, null); installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; String apkName = getNextCodePath(null, pkgName, ".apk"); codeFileName = new File(installDir, apkName + ".apk").getPath(); @@ -9179,14 +9225,26 @@ public class PackageManagerService extends IPackageManager.Stub { NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile); nativeLibraryFile.delete(); } + + final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codeFile); + String[] abiList = (abiOverride != null) ? + new String[] { abiOverride } : Build.SUPPORTED_ABIS; try { - int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile); + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && + abiOverride == null && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } + + int copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList); if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { return copyRet; } } catch (IOException e) { Slog.e(TAG, "Copying native libraries failed", e); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } finally { + handle.close(); } return ret; @@ -9401,14 +9459,15 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.observer2, params.flags, params.installerPackageName, params.getManifestDigest(), - params.getUser(), null /* instruction set */); + params.getUser(), params.packageInstructionSetOverride, + params.packageAbiOverride); } AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, String instructionSet, boolean isExternal, boolean isForwardLocked) { super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null, instructionSet); + null, null, null, instructionSet, null); // Extract cid from fullCodePath int eidx = fullCodePath.lastIndexOf("/"); String subStr1 = fullCodePath.substring(0, eidx); @@ -9420,7 +9479,7 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) { super(null, null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null, instructionSet); + null, null, null, instructionSet, null); this.cid = cid; setCachePath(PackageHelper.getSdDir(cid)); } @@ -9429,7 +9488,7 @@ public class PackageManagerService extends IPackageManager.Stub { boolean isExternal, boolean isForwardLocked) { super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null, instructionSet); + null, null, null, instructionSet, null); this.cid = cid; } @@ -9441,7 +9500,7 @@ public class PackageManagerService extends IPackageManager.Stub { try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkExternalFreeStorage(packageURI, isFwdLocked()); + return imcs.checkExternalFreeStorage(packageURI, isFwdLocked(), abiOverride); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -9467,7 +9526,8 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(), - RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked()); + RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked(), + abiOverride); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -9775,7 +9835,7 @@ public class PackageManagerService extends IPackageManager.Stub { */ private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, - String installerPackageName, PackageInstalledInfo res) { + String installerPackageName, PackageInstalledInfo res, String abiOverride) { // Remember this for later, in case we need to rollback this install String pkgName = pkg.packageName; @@ -9803,7 +9863,7 @@ public class PackageManagerService extends IPackageManager.Stub { } mLastScanError = PackageManager.INSTALL_SUCCEEDED; PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, - System.currentTimeMillis(), user); + System.currentTimeMillis(), user, abiOverride); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -9830,7 +9890,7 @@ public class PackageManagerService extends IPackageManager.Stub { private void replacePackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, - String installerPackageName, PackageInstalledInfo res) { + String installerPackageName, PackageInstalledInfo res, String abiOverride) { PackageParser.Package oldPackage; String pkgName = pkg.packageName; @@ -9859,17 +9919,19 @@ public class PackageManagerService extends IPackageManager.Stub { boolean sysPkg = (isSystemApp(oldPackage)); if (sysPkg) { replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, - user, allUsers, perUserInstalled, installerPackageName, res); + user, allUsers, perUserInstalled, installerPackageName, res, + abiOverride); } else { replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, - user, allUsers, perUserInstalled, installerPackageName, res); + user, allUsers, perUserInstalled, installerPackageName, res, + abiOverride); } } private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, int[] allUsers, boolean[] perUserInstalled, - String installerPackageName, PackageInstalledInfo res) { + String installerPackageName, PackageInstalledInfo res, String abiOverride) { PackageParser.Package newPackage = null; String pkgName = deletedPackage.packageName; boolean deletedPkg = true; @@ -9894,7 +9956,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Successfully deleted the old package. Now proceed with re-installation mLastScanError = PackageManager.INSTALL_SUCCEEDED; newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME, - System.currentTimeMillis(), user); + System.currentTimeMillis(), user, abiOverride); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -9923,7 +9985,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // Since we failed to install the new package we need to restore the old // package that we deleted. - if(deletedPkg) { + if (deletedPkg) { if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage); File restoreFile = new File(deletedPackage.mPath); // Parse old package @@ -9934,7 +9996,7 @@ public class PackageManagerService extends IPackageManager.Stub { int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME; if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode, - origUpdateTime, null) == null) { + origUpdateTime, null, null) == null) { Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade"); return; } @@ -9954,7 +10016,7 @@ public class PackageManagerService extends IPackageManager.Stub { private void replaceSystemPackageLI(PackageParser.Package deletedPackage, PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, int[] allUsers, boolean[] perUserInstalled, - String installerPackageName, PackageInstalledInfo res) { + String installerPackageName, PackageInstalledInfo res, String abiOverride) { if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg + ", old=" + deletedPackage); PackageParser.Package newPackage = null; @@ -10008,7 +10070,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Successfully disabled the old package. Now proceed with re-installation res.returnCode = mLastScanError = PackageManager.INSTALL_SUCCEEDED; pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user); + newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user, abiOverride); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -10042,7 +10104,7 @@ public class PackageManagerService extends IPackageManager.Stub { removeInstalledPackageLI(newPackage, true); } // Add back the old system package - scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user); + scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user, null); // Restore the old system information in Settings synchronized(mPackages) { if (updatedSettings) { @@ -10276,10 +10338,10 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); if (replace) { replacePackageLI(pkg, parseFlags, scanMode, args.user, - installerPackageName, res); + installerPackageName, res, args.abiOverride); } else { installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user, - installerPackageName, res); + installerPackageName, res, args.abiOverride); } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); @@ -10696,7 +10758,7 @@ public class PackageManagerService extends IPackageManager.Stub { parseFlags |= PackageParser.PARSE_IS_PRIVILEGED; } PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath, - parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null); + parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null, null); if (newPkg == null) { Slog.w(TAG, "Failed to restore system package:" + newPs.name @@ -11475,33 +11537,34 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig, - int userIdDest) { + public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, + int sourceUserId, int targetUserId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); if (filter.countActions() == 0) { - Slog.w(TAG, "Cannot set a forwarding intent filter with no filter actions"); + Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions"); return; } synchronized (mPackages) { - mSettings.editForwardingIntentResolverLPw(userIdOrig).addFilter( - new ForwardingIntentFilter(filter, removable, userIdDest)); - mSettings.writePackageRestrictionsLPr(userIdOrig); + mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter( + new CrossProfileIntentFilter(filter, removable, targetUserId)); + mSettings.writePackageRestrictionsLPr(sourceUserId); } } @Override - public void clearForwardingIntentFilters(int userIdOrig) { + public void clearCrossProfileIntentFilters(int sourceUserId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); synchronized (mPackages) { - ForwardingIntentResolver fir = mSettings.editForwardingIntentResolverLPw(userIdOrig); - HashSet<ForwardingIntentFilter> set = - new HashSet<ForwardingIntentFilter>(fir.filterSet()); - for (ForwardingIntentFilter fif : set) { - if (fif.isRemovable()) fir.removeFilter(fif); + CrossProfileIntentResolver cpir = + mSettings.editCrossProfileIntentResolverLPw(sourceUserId); + HashSet<CrossProfileIntentFilter> set = + new HashSet<CrossProfileIntentFilter>(cpir.filterSet()); + for (CrossProfileIntentFilter cpif : set) { + if (cpif.isRemovable()) cpir.removeFilter(cpif); } - mSettings.writePackageRestrictionsLPr(userIdOrig); + mSettings.writePackageRestrictionsLPr(sourceUserId); } } @@ -12508,7 +12571,7 @@ public class PackageManagerService extends IPackageManager.Stub { doGc = true; synchronized (mInstallLock) { final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags, - 0, 0, null); + 0, 0, null, null); // Scan the package if (pkg != null) { /* diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index d70c725..c78249b 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -52,25 +52,49 @@ public final class SELinuxMMAC { private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false; // Signature seinfo values read from policy. - private static HashMap<Signature, Policy> sSigSeinfo = - new HashMap<Signature, Policy>(); + private static HashMap<Signature, Policy> sSigSeinfo = new HashMap<Signature, Policy>(); // Default seinfo read from policy. private static String sDefaultSeinfo = null; - // Locations of potential install policy files. - private static final File[] INSTALL_POLICY_FILE = { - new File(Environment.getDataDirectory(), "security/mac_permissions.xml"), - new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"), - null}; + // Data policy override version file. + private static final String DATA_VERSION_FILE = + Environment.getDataDirectory() + "/security/current/selinux_version"; - // Location of seapp_contexts policy file. - private static final String SEAPP_CONTEXTS_FILE = "/seapp_contexts"; + // Base policy version file. + private static final String BASE_VERSION_FILE = "/selinux_version"; + + // Whether override security policies should be loaded. + private static final boolean USE_OVERRIDE_POLICY = useOverridePolicy(); + + // Data override mac_permissions.xml policy file. + private static final String DATA_MAC_PERMISSIONS = + Environment.getDataDirectory() + "/security/current/mac_permissions.xml"; + + // Base mac_permissions.xml policy file. + private static final String BASE_MAC_PERMISSIONS = + Environment.getRootDirectory() + "/etc/security/mac_permissions.xml"; + + // Determine which mac_permissions.xml file to use. + private static final String MAC_PERMISSIONS = USE_OVERRIDE_POLICY ? + DATA_MAC_PERMISSIONS : BASE_MAC_PERMISSIONS; + + // Data override seapp_contexts policy file. + private static final String DATA_SEAPP_CONTEXTS = + Environment.getDataDirectory() + "/security/current/seapp_contexts"; + + // Base seapp_contexts policy file. + private static final String BASE_SEAPP_CONTEXTS = "/seapp_contexts"; + + // Determine which seapp_contexts file to use. + private static final String SEAPP_CONTEXTS = USE_OVERRIDE_POLICY ? + DATA_SEAPP_CONTEXTS : BASE_SEAPP_CONTEXTS; // Stores the hash of the last used seapp_contexts file. private static final String SEAPP_HASH_FILE = Environment.getDataDirectory().toString() + "/system/seapp_hash"; + // Signature policy stanzas static class Policy { private String seinfo; @@ -112,51 +136,17 @@ public final class SELinuxMMAC { sDefaultSeinfo = null; } - /** - * Parses an MMAC install policy from a predefined list of locations. - * @return boolean indicating whether an install policy was correctly parsed. - */ public static boolean readInstallPolicy() { - - return readInstallPolicy(INSTALL_POLICY_FILE); - } - - /** - * Parses an MMAC install policy given as an argument. - * @param policyFile object representing the path of the policy. - * @return boolean indicating whether the install policy was correctly parsed. - */ - public static boolean readInstallPolicy(File policyFile) { - - return readInstallPolicy(new File[]{policyFile,null}); - } - - private static boolean readInstallPolicy(File[] policyFiles) { // Temp structures to hold the rules while we parse the xml file. // We add all the rules together once we know there's no structural problems. HashMap<Signature, Policy> sigSeinfo = new HashMap<Signature, Policy>(); String defaultSeinfo = null; FileReader policyFile = null; - int i = 0; - while (policyFile == null && policyFiles != null && policyFiles[i] != null) { - try { - policyFile = new FileReader(policyFiles[i]); - break; - } catch (FileNotFoundException e) { - Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath()); - } - i++; - } - - if (policyFile == null) { - Slog.d(TAG, "No policy file found. All seinfo values will be null."); - return false; - } - - Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath()); - try { + policyFile = new FileReader(MAC_PERMISSIONS); + Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS); + XmlPullParser parser = Xml.newPullParser(); parser.setInput(policyFile); @@ -199,20 +189,14 @@ public final class SELinuxMMAC { XmlUtils.skipCurrentTag(parser); } } - } catch (XmlPullParserException e) { - // An error outside of a stanza means a structural problem - // with the xml file. So ignore it. - Slog.w(TAG, "Got exception parsing ", e); + } catch (XmlPullParserException xpe) { + Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, xpe); return false; - } catch (IOException e) { - Slog.w(TAG, "Got exception parsing ", e); + } catch (IOException ioe) { + Slog.w(TAG, "Got exception parsing " + MAC_PERMISSIONS, ioe); return false; } finally { - try { - policyFile.close(); - } catch (IOException e) { - //omit - } + IoUtils.closeQuietly(policyFile); } flushInstallPolicy(); @@ -412,7 +396,7 @@ public final class SELinuxMMAC { // Any error with the seapp_contexts file should be fatal byte[] currentHash = null; try { - currentHash = returnHash(SEAPP_CONTEXTS_FILE); + currentHash = returnHash(SEAPP_CONTEXTS); } catch (IOException ioe) { Slog.e(TAG, "Error with hashing seapp_contexts.", ioe); return false; @@ -434,7 +418,7 @@ public final class SELinuxMMAC { */ public static void setRestoreconDone() { try { - final byte[] currentHash = returnHash(SEAPP_CONTEXTS_FILE); + final byte[] currentHash = returnHash(SEAPP_CONTEXTS); dumpHash(new File(SEAPP_HASH_FILE), currentHash); } catch (IOException ioe) { Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe); @@ -485,4 +469,21 @@ public final class SELinuxMMAC { throw new RuntimeException(nsae); // impossible } } + + private static boolean useOverridePolicy() { + try { + final String overrideVersion = IoUtils.readFileAsString(DATA_VERSION_FILE); + final String baseVersion = IoUtils.readFileAsString(BASE_VERSION_FILE); + if (overrideVersion.equals(baseVersion)) { + return true; + } + Slog.e(TAG, "Override policy version '" + overrideVersion + "' doesn't match " + + "base version '" + baseVersion + "'. Skipping override policy files."); + } catch (FileNotFoundException fnfe) { + // Override version file doesn't have to exist so silently ignore. + } catch (IOException ioe) { + Slog.w(TAG, "Skipping override policy files.", ioe); + } + return false; + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 3ca658f..3483fae 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -132,6 +132,9 @@ final class Settings { private static final String TAG_PACKAGE = "pkg"; private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES = "persistent-preferred-activities"; + static final String TAG_CROSS_PROFILE_INTENT_FILTERS = + "crossProfile-intent-filters"; + //Old name. Kept for compatibility static final String TAG_FORWARDING_INTENT_FILTERS = "forwarding-intent-filters"; @@ -189,8 +192,8 @@ final class Settings { new SparseArray<PersistentPreferredIntentResolver>(); // For every user, it is used to find to which other users the intent can be forwarded. - final SparseArray<ForwardingIntentResolver> mForwardingIntentResolvers = - new SparseArray<ForwardingIntentResolver>(); + final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers = + new SparseArray<CrossProfileIntentResolver>(); final HashMap<String, SharedUserSetting> mSharedUsers = new HashMap<String, SharedUserSetting>(); @@ -856,13 +859,13 @@ final class Settings { return ppir; } - ForwardingIntentResolver editForwardingIntentResolverLPw(int userId) { - ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId); - if (fir == null) { - fir = new ForwardingIntentResolver(); - mForwardingIntentResolvers.put(userId, fir); + CrossProfileIntentResolver editCrossProfileIntentResolverLPw(int userId) { + CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId); + if (cpir == null) { + cpir = new CrossProfileIntentResolver(); + mCrossProfileIntentResolvers.put(userId, cpir); } - return fir; + return cpir; } private File getUserPackagesStateFile(int userId) { @@ -980,7 +983,7 @@ final class Settings { } } - private void readForwardingIntentFiltersLPw(XmlPullParser parser, int userId) + private void readCrossProfileIntentFiltersLPw(XmlPullParser parser, int userId) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; @@ -991,10 +994,10 @@ final class Settings { } String tagName = parser.getName(); if (tagName.equals(TAG_ITEM)) { - ForwardingIntentFilter fif = new ForwardingIntentFilter(parser); - editForwardingIntentResolverLPw(userId).addFilter(fif); + CrossProfileIntentFilter cpif = new CrossProfileIntentFilter(parser); + editCrossProfileIntentResolverLPw(userId).addFilter(cpif); } else { - String msg = "Unknown element under " + TAG_FORWARDING_INTENT_FILTERS + ": " + + String msg = "Unknown element under " + TAG_CROSS_PROFILE_INTENT_FILTERS + ": " + parser.getName(); PackageManagerService.reportSettingsProblem(Log.WARN, msg); XmlUtils.skipCurrentTag(parser); @@ -1130,8 +1133,9 @@ final class Settings { readPreferredActivitiesLPw(parser, userId); } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { readPersistentPreferredActivitiesLPw(parser, userId); - } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) { - readForwardingIntentFiltersLPw(parser, userId); + } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS) + || tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) { + readCrossProfileIntentFiltersLPw(parser, userId); } else { Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: " + parser.getName()); @@ -1209,18 +1213,18 @@ final class Settings { serializer.endTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES); } - void writeForwardingIntentFiltersLPr(XmlSerializer serializer, int userId) + void writeCrossProfileIntentFiltersLPr(XmlSerializer serializer, int userId) throws IllegalArgumentException, IllegalStateException, IOException { - serializer.startTag(null, TAG_FORWARDING_INTENT_FILTERS); - ForwardingIntentResolver fir = mForwardingIntentResolvers.get(userId); - if (fir != null) { - for (final ForwardingIntentFilter fif : fir.filterSet()) { + serializer.startTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS); + CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId); + if (cpir != null) { + for (final CrossProfileIntentFilter cpif : cpir.filterSet()) { serializer.startTag(null, TAG_ITEM); - fif.writeToXml(serializer); + cpif.writeToXml(serializer); serializer.endTag(null, TAG_ITEM); } } - serializer.endTag(null, TAG_FORWARDING_INTENT_FILTERS); + serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS); } void writePackageRestrictionsLPr(int userId) { @@ -1321,7 +1325,7 @@ final class Settings { writePersistentPreferredActivitiesLPr(serializer, userId); - writeForwardingIntentFiltersLPr(serializer, userId); + writeCrossProfileIntentFiltersLPr(serializer, userId); serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS); @@ -1940,10 +1944,11 @@ final class Settings { // TODO: check whether this is okay! as it is very // similar to how preferred-activities are treated readPersistentPreferredActivitiesLPw(parser, 0); - } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)) { + } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS) + || tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) { // TODO: check whether this is okay! as it is very // similar to how preferred-activities are treated - readForwardingIntentFiltersLPw(parser, 0); + readCrossProfileIntentFiltersLPw(parser, 0); } else if (tagName.equals("updated-package")) { readDisabledSysPackageLPw(parser); } else if (tagName.equals("cleaning-package")) { @@ -2935,6 +2940,26 @@ final class Settings { file.delete(); file = getUserPackagesStateBackupFile(userId); file.delete(); + removeCrossProfileIntentFiltersToUserLPr(userId); + } + + void removeCrossProfileIntentFiltersToUserLPr(int targetUserId) { + for (int i = 0; i < mCrossProfileIntentResolvers.size(); i++) { + int sourceUserId = mCrossProfileIntentResolvers.keyAt(i); + CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId); + boolean needsWriting = false; + HashSet<CrossProfileIntentFilter> cpifs = + new HashSet<CrossProfileIntentFilter>(cpir.filterSet()); + for (CrossProfileIntentFilter cpif : cpifs) { + if (cpif.getTargetUserId() == targetUserId) { + needsWriting = true; + cpir.removeFilter(cpif); + } + } + if (needsWriting) { + writePackageRestrictionsLPr(sourceUserId); + } + } } // This should be called (at least) whenever an application is removed diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 102b2d4..7162683 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -107,6 +107,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_TYPE_STRING_ARRAY = "sa"; private static final String ATTR_TYPE_STRING = "s"; private static final String ATTR_TYPE_BOOLEAN = "b"; + private static final String ATTR_TYPE_INTEGER = "i"; private static final String USER_INFO_DIR = "system" + File.separator + "users"; private static final String USER_LIST_FILENAME = "userlist.xml"; @@ -1140,53 +1141,57 @@ public class UserManagerService extends IUserManager.Stub { */ public boolean removeUser(int userHandle) { checkManageUsersPermission("Only the system can remove users"); - final UserInfo user; - synchronized (mPackagesLock) { - user = mUsers.get(userHandle); - if (userHandle == 0 || user == null) { - return false; + long ident = Binder.clearCallingIdentity(); + try { + final UserInfo user; + synchronized (mPackagesLock) { + user = mUsers.get(userHandle); + if (userHandle == 0 || user == null) { + return false; + } + mRemovingUserIds.put(userHandle, true); + try { + mAppOpsService.removeUser(userHandle); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e); + } + // Set this to a partially created user, so that the user will be purged + // on next startup, in case the runtime stops now before stopping and + // removing the user completely. + user.partial = true; + // Mark it as disabled, so that it isn't returned any more when + // profiles are queried. + user.flags |= UserInfo.FLAG_DISABLED; + writeUserLocked(user); + } + + if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID + && user.isManagedProfile()) { + // Send broadcast to notify system that the user removed was a + // managed user. + sendProfileRemovedBroadcast(user.profileGroupId, user.id); } - mRemovingUserIds.put(userHandle, true); + + if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); + int res; try { - mAppOpsService.removeUser(userHandle); + res = ActivityManagerNative.getDefault().stopUser(userHandle, + new IStopUserCallback.Stub() { + @Override + public void userStopped(int userId) { + finishRemoveUser(userId); + } + @Override + public void userStopAborted(int userId) { + } + }); } catch (RemoteException e) { - Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e); + return false; } - // Set this to a partially created user, so that the user will be purged - // on next startup, in case the runtime stops now before stopping and - // removing the user completely. - user.partial = true; - // Mark it as disabled, so that it isn't returned any more when - // profiles are queried. - user.flags |= UserInfo.FLAG_DISABLED; - writeUserLocked(user); - } - - if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID - && user.isManagedProfile()) { - // Send broadcast to notify system that the user removed was a - // managed user. - sendProfileRemovedBroadcast(user.profileGroupId, user.id); - } - - if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); - int res; - try { - res = ActivityManagerNative.getDefault().stopUser(userHandle, - new IStopUserCallback.Stub() { - @Override - public void userStopped(int userId) { - finishRemoveUser(userId); - } - @Override - public void userStopAborted(int userId) { - } - }); - } catch (RemoteException e) { - return false; + return res == ActivityManager.USER_OP_SUCCESS; + } finally { + Binder.restoreCallingIdentity(ident); } - - return res == ActivityManager.USER_OP_SUCCESS; } void finishRemoveUser(final int userHandle) { @@ -1528,16 +1533,18 @@ public class UserManagerService extends IUserManager.Stub { String [] valueStrings = new String[values.size()]; values.toArray(valueStrings); restrictions.putStringArray(key, valueStrings); - } else if (ATTR_TYPE_BOOLEAN.equals(valType)) { - restrictions.putBoolean(key, Boolean.parseBoolean( - parser.nextText().trim())); } else { String value = parser.nextText().trim(); - restrictions.putString(key, value); + if (ATTR_TYPE_BOOLEAN.equals(valType)) { + restrictions.putBoolean(key, Boolean.parseBoolean(value)); + } else if (ATTR_TYPE_INTEGER.equals(valType)) { + restrictions.putInt(key, Integer.parseInt(value)); + } else { + restrictions.putString(key, value); + } } } } - } catch (IOException ioe) { } catch (XmlPullParserException pe) { } finally { @@ -1577,6 +1584,9 @@ public class UserManagerService extends IUserManager.Stub { if (value instanceof Boolean) { serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN); serializer.text(value.toString()); + } else if (value instanceof Integer) { + serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER); + serializer.text(value.toString()); } else if (value == null || value instanceof String) { serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING); serializer.text(value != null ? (String) value : ""); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 986cdc1..1629a61 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -40,6 +40,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -326,8 +327,12 @@ public class TrustManagerService extends SystemService { for (int i = 0; i < mTrustListeners.size(); i++) { try { mTrustListeners.get(i).onTrustChanged(enabled, userId); + } catch (DeadObjectException e) { + if (DEBUG) Slog.d(TAG, "Removing dead TrustListener."); + mTrustListeners.remove(i); + i--; } catch (RemoteException e) { - Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e); + Slog.e(TAG, "Exception while notifying TrustListener.", e); } } } diff --git a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java index 3c960c7..55dd4ab 100644 --- a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java +++ b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java @@ -40,12 +40,20 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { private static final String fileContextsPath = "file_contexts"; private static final String propertyContextsPath = "property_contexts"; private static final String seappContextsPath = "seapp_contexts"; + private static final String versionPath = "selinux_version"; + private static final String macPermissionsPath = "mac_permissions.xml"; public SELinuxPolicyInstallReceiver() { super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version"); } private void backupContexts(File contexts) { + new File(contexts, versionPath).renameTo( + new File(contexts, versionPath + "_backup")); + + new File(contexts, macPermissionsPath).renameTo( + new File(contexts, macPermissionsPath + "_backup")); + new File(contexts, seappContextsPath).renameTo( new File(contexts, seappContextsPath + "_backup")); @@ -60,6 +68,8 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { } private void copyUpdate(File contexts) { + new File(updateDir, versionPath).renameTo(new File(contexts, versionPath)); + new File(updateDir, macPermissionsPath).renameTo(new File(contexts, macPermissionsPath)); new File(updateDir, seappContextsPath).renameTo(new File(contexts, seappContextsPath)); new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath)); new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath)); @@ -75,11 +85,13 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { } private int[] readChunkLengths(BufferedInputStream bundle) throws IOException { - int[] chunks = new int[4]; + int[] chunks = new int[6]; chunks[0] = readInt(bundle); chunks[1] = readInt(bundle); chunks[2] = readInt(bundle); chunks[3] = readInt(bundle); + chunks[4] = readInt(bundle); + chunks[5] = readInt(bundle); return chunks; } @@ -94,10 +106,12 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent)); try { int[] chunkLengths = readChunkLengths(stream); - installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]); - installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]); - installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]); - installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]); + installFile(new File(updateDir, versionPath), stream, chunkLengths[0]); + installFile(new File(updateDir, macPermissionsPath), stream, chunkLengths[1]); + installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[2]); + installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[3]); + installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[4]); + installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[5]); } finally { IoUtils.closeQuietly(stream); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7382f4c..05502cf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5826,7 +5826,9 @@ public class WindowManagerService extends IWindowManager.Stub // whether the screenshot should use the identity transformation matrix // (e.g., enable it when taking a screenshot for recents, since we might be in // the middle of the rotation animation, but don't want a rotated recent image). - rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer, false); + // TODO: Replace 'new Rect()' with the portion of the screen to capture for the + // screenshot. + rawss = SurfaceControl.screenshot(new Rect(), dw, dh, minLayer, maxLayer, false); } } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES); if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " + diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp index 163225e..9a5079d 100644 --- a/services/core/jni/com_android_server_AssetAtlasService.cpp +++ b/services/core/jni/com_android_server_AssetAtlasService.cpp @@ -18,6 +18,7 @@ #include "jni.h" #include "JNIHelp.h" +#include "android/graphics/GraphicsJNI.h" #include <android_view_GraphicBuffer.h> #include <cutils/log.h> @@ -46,7 +47,7 @@ namespace android { // ---------------------------------------------------------------------------- static struct { - jmethodID safeCanvasSwap; + jmethodID setNativeBitmap; } gCanvasClassInfo; #define INVOKEV(object, method, ...) \ @@ -63,9 +64,7 @@ static jlong com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, job bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height); bitmap->allocPixels(); bitmap->eraseColor(0); - - SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (*bitmap)); - INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); + INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap)); return reinterpret_cast<jlong>(bitmap); } @@ -74,8 +73,7 @@ static void com_android_server_AssetAtlasService_releaseCanvas(JNIEnv* env, jobj jobject canvas, jlong bitmapHandle) { SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkCanvas* nativeCanvas = SkNEW(SkCanvas); - INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); + INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, (jlong)0); delete bitmap; } @@ -244,7 +242,7 @@ int register_android_server_AssetAtlasService(JNIEnv* env) { jclass clazz; FIND_CLASS(clazz, "android/graphics/Canvas"); - GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V"); + GET_METHOD_ID(gCanvasClassInfo.setNativeBitmap, clazz, "setNativeBitmap", "(J)V"); return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5395f60..a52396e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -130,6 +130,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final boolean DBG = false; + private static final String ATTR_PERMISSION_PROVIDER = "permission-provider"; + final Context mContext; final UserManager mUserManager; final PowerManager.WakeLock mWakeLock; @@ -190,6 +192,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // This is the list of component allowed to start lock task mode. final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>(); + ComponentName mRestrictionsProvider; + public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } @@ -944,6 +948,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.startDocument(null, true); out.startTag(null, "policies"); + if (policy.mRestrictionsProvider != null) { + out.attribute(null, ATTR_PERMISSION_PROVIDER, + policy.mRestrictionsProvider.flattenToString()); + } final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { @@ -1039,6 +1047,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new XmlPullParserException( "Settings do not start with policies tag: found " + tag); } + + // Extract the permission provider component name if available + String permissionProvider = parser.getAttributeValue(null, ATTR_PERMISSION_PROVIDER); + if (permissionProvider != null) { + policy.mRestrictionsProvider = ComponentName.unflattenFromString(permissionProvider); + } + type = parser.next(); int outerDepth = parser.getDepth(); policy.mLockTaskComponents.clear(); @@ -3303,7 +3318,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void addForwardingIntentFilter(ComponentName who, IntentFilter filter, int flags) { + @Override + public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) { + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + int userHandle = UserHandle.getCallingUserId(); + DevicePolicyData userData = getUserData(userHandle); + userData.mRestrictionsProvider = permissionProvider; + saveSettingsLocked(userHandle); + } + } + + @Override + public ComponentName getRestrictionsProvider(int userHandle) { + synchronized (this) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system can query the permission provider"); + } + DevicePolicyData userData = getUserData(userHandle); + return userData != null ? userData.mRestrictionsProvider : null; + } + } + + public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) { int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { if (who == null) { @@ -3314,12 +3355,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { IPackageManager pm = AppGlobals.getPackageManager(); long id = Binder.clearCallingIdentity(); try { - if ((flags & DevicePolicyManager.FLAG_TO_PRIMARY_USER) != 0) { - pm.addForwardingIntentFilter(filter, true /*removable*/, callingUserId, + if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) { + pm.addCrossProfileIntentFilter(filter, true /*removable*/, callingUserId, UserHandle.USER_OWNER); } - if ((flags & DevicePolicyManager.FLAG_TO_MANAGED_PROFILE) != 0) { - pm.addForwardingIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER, + if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) { + pm.addCrossProfileIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER, callingUserId); } } catch (RemoteException re) { @@ -3330,7 +3371,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void clearForwardingIntentFilters(ComponentName who) { + public void clearCrossProfileIntentFilters(ComponentName who) { int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { if (who == null) { @@ -3340,8 +3381,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { IPackageManager pm = AppGlobals.getPackageManager(); long id = Binder.clearCallingIdentity(); try { - pm.clearForwardingIntentFilters(callingUserId); - pm.clearForwardingIntentFilters(UserHandle.USER_OWNER); + pm.clearCrossProfileIntentFilters(callingUserId); + pm.clearCrossProfileIntentFilters(UserHandle.USER_OWNER); } catch (RemoteException re) { // Shouldn't happen } finally { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e8b7b69..18ece5b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -80,6 +80,7 @@ import com.android.server.pm.PackageManagerService; import com.android.server.pm.UserManagerService; import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; +import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.search.SearchManagerService; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.storage.DeviceStorageMonitorService; @@ -126,7 +127,7 @@ public final class SystemServer { private static final String WIFI_SERVICE_CLASS = "com.android.server.wifi.WifiService"; private static final String WIFI_PASSPOINT_SERVICE_CLASS = - "com.android.server.wifi.passpoint.PasspointService"; + "com.android.server.wifi.passpoint.WifiPasspointService"; private static final String WIFI_P2P_SERVICE_CLASS = "com.android.server.wifi.p2p.WifiP2pService"; private static final String HDMI_CEC_SERVICE_CLASS = @@ -330,7 +331,6 @@ public final class SystemServer { IPackageManager pm = null; WindowManagerService wm = null; BluetoothManagerService bluetooth = null; - DockObserver dock = null; UsbService usb = null; SerialService serial = null; RecognitionManagerService recognition = null; @@ -644,15 +644,15 @@ public final class SystemServer { } try { - mSystemServiceManager.startService(WIFI_SERVICE_CLASS); + mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS); } catch (Throwable e) { - reportWtf("starting Wi-Fi Service", e); + reportWtf("starting Wi-Fi PasspointService", e); } try { - mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS); + mSystemServiceManager.startService(WIFI_SERVICE_CLASS); } catch (Throwable e) { - reportWtf("starting Wi-Fi PasspointService", e); + reportWtf("starting Wi-Fi Service", e); } try { @@ -789,13 +789,7 @@ public final class SystemServer { } if (!disableNonCoreServices) { - try { - Slog.i(TAG, "Dock Observer"); - // Listen for dock station changes - dock = new DockObserver(context); - } catch (Throwable e) { - reportWtf("starting DockObserver", e); - } + mSystemServiceManager.startService(DockObserver.class); } if (!disableMedia) { @@ -948,6 +942,12 @@ public final class SystemServer { } try { + mSystemServiceManager.startService(RestrictionsManagerService.class); + } catch (Throwable e) { + reportWtf("starting RestrictionsManagerService", e); + } + + try { mSystemServiceManager.startService(MediaSessionService.class); } catch (Throwable e) { reportWtf("starting MediaSessionService", e); @@ -998,8 +998,7 @@ public final class SystemServer { try { Slog.i(TAG, "LauncherAppsService"); - LauncherAppsService las = new LauncherAppsService(context); - ServiceManager.addService(Context.LAUNCHER_APPS_SERVICE, las); + mSystemServiceManager.startService(LauncherAppsService.class); } catch (Throwable t) { reportWtf("starting LauncherAppsService", t); } @@ -1085,7 +1084,6 @@ public final class SystemServer { final NetworkPolicyManagerService networkPolicyF = networkPolicy; final ConnectivityService connectivityF = connectivity; final NetworkScoreService networkScoreF = networkScore; - final DockObserver dockF = dock; final WallpaperManagerService wallpaperF = wallpaper; final InputMethodManagerService immF = imm; final RecognitionManagerService recognitionF = recognition; @@ -1159,11 +1157,6 @@ public final class SystemServer { reportWtf("making Connectivity Service ready", e); } try { - if (dockF != null) dockF.systemReady(); - } catch (Throwable e) { - reportWtf("making Dock Service ready", e); - } - try { if (recognitionF != null) recognitionF.systemReady(); } catch (Throwable e) { reportWtf("making Recognition Service ready", e); diff --git a/services/restrictions/Android.mk b/services/restrictions/Android.mk new file mode 100644 index 0000000..fcf8626 --- /dev/null +++ b/services/restrictions/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := services.restrictions + +LOCAL_SRC_FILES += \ + $(call all-java-files-under,java) + +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java new file mode 100644 index 0000000..e1f77b3 --- /dev/null +++ b/services/restrictions/java/com/android/server/restrictions/RestrictionsManagerService.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.restrictions; + +import android.Manifest; +import android.app.AppGlobals; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.admin.IDevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IRestrictionsManager; +import android.content.RestrictionsManager; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.IUserManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.internal.util.ArrayUtils; +import com.android.server.SystemService; + +/** + * SystemService wrapper for the RestrictionsManager implementation. Publishes the + * Context.RESTRICTIONS_SERVICE. + */ + +public final class RestrictionsManagerService extends SystemService { + private final RestrictionsManagerImpl mRestrictionsManagerImpl; + + public RestrictionsManagerService(Context context) { + super(context); + mRestrictionsManagerImpl = new RestrictionsManagerImpl(context); + } + + @Override + public void onStart() { + publishBinderService(Context.RESTRICTIONS_SERVICE, mRestrictionsManagerImpl); + } + + class RestrictionsManagerImpl extends IRestrictionsManager.Stub { + private final Context mContext; + private final IUserManager mUm; + private final IDevicePolicyManager mDpm; + + public RestrictionsManagerImpl(Context context) { + mContext = context; + mUm = (IUserManager) getBinderService(Context.USER_SERVICE); + mDpm = (IDevicePolicyManager) getBinderService(Context.DEVICE_POLICY_SERVICE); + } + + @Override + public Bundle getApplicationRestrictions(String packageName) throws RemoteException { + return mUm.getApplicationRestrictions(packageName); + } + + @Override + public boolean hasRestrictionsProvider() throws RemoteException { + int userHandle = UserHandle.getCallingUserId(); + if (mDpm != null) { + long ident = Binder.clearCallingIdentity(); + try { + return mDpm.getRestrictionsProvider(userHandle) != null; + } finally { + Binder.restoreCallingIdentity(ident); + } + } else { + return false; + } + } + + @Override + public void requestPermission(String packageName, String requestTemplate, + Bundle requestData) throws RemoteException { + int callingUid = Binder.getCallingUid(); + int userHandle = UserHandle.getUserId(callingUid); + if (mDpm != null) { + long ident = Binder.clearCallingIdentity(); + try { + ComponentName restrictionsProvider = + mDpm.getRestrictionsProvider(userHandle); + // Check if there is a restrictions provider + if (restrictionsProvider == null) { + throw new IllegalStateException( + "Cannot request permission without a restrictions provider registered"); + } + // Check that the packageName matches the caller. + enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" + + " match caller "); + // Prepare and broadcast the intent to the provider + Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION); + intent.setComponent(restrictionsProvider); + intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName); + intent.putExtra(RestrictionsManager.EXTRA_TEMPLATE_ID, requestTemplate); + intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData); + mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + private void enforceCallerMatchesPackage(int callingUid, String packageName, + String message) { + try { + String[] pkgs = AppGlobals.getPackageManager().getPackagesForUid(callingUid); + if (pkgs != null) { + if (!ArrayUtils.contains(pkgs, packageName)) { + throw new SecurityException(message + callingUid); + } + } + } catch (RemoteException re) { + // Shouldn't happen + } + } + + @Override + public void notifyPermissionResponse(String packageName, Bundle response) + throws RemoteException { + // Check caller + int callingUid = Binder.getCallingUid(); + int userHandle = UserHandle.getUserId(callingUid); + if (mDpm != null) { + long ident = Binder.clearCallingIdentity(); + try { + ComponentName permProvider = mDpm.getRestrictionsProvider(userHandle); + if (permProvider == null) { + throw new SecurityException("No restrictions provider registered for user"); + } + enforceCallerMatchesPackage(callingUid, permProvider.getPackageName(), + "Restrictions provider does not match caller "); + + // Post the response to target package + Intent responseIntent = new Intent( + RestrictionsManager.ACTION_PERMISSION_RESPONSE_RECEIVED); + responseIntent.setPackage(packageName); + responseIntent.putExtra(RestrictionsManager.EXTRA_RESPONSE_BUNDLE, response); + mContext.sendBroadcastAsUser(responseIntent, new UserHandle(userHandle)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + } +} diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 7848b1d..636dd4d 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -35,7 +35,8 @@ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> - + <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> + <application> <uses-library android:name="android.test.runner" /> @@ -53,6 +54,15 @@ </intent-filter> </service> + <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver" + android:permission="android.permission.BIND_DEVICE_ADMIN"> + <meta-data android:name="android.app.device_admin" + android:resource="@xml/device_admin_sample" /> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + </intent-filter> + </receiver> + </application> <instrumentation diff --git a/services/tests/servicestests/res/xml/device_admin_sample.xml b/services/tests/servicestests/res/xml/device_admin_sample.xml new file mode 100644 index 0000000..032debb --- /dev/null +++ b/services/tests/servicestests/res/xml/device_admin_sample.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<device-admin xmlns:android="http://schemas.android.com/apk/res/android"> + <uses-policies> + <limit-password /> + <watch-login /> + <reset-password /> + <force-lock /> + <wipe-data /> + <expire-password /> + <encrypted-storage /> + <disable-camera /> + <disable-keyguard-features /> + </uses-policies> +</device-admin> diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java new file mode 100644 index 0000000..8e8e4e6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.devicepolicy; + +import android.app.admin.DeviceAdminReceiver; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +/** + * Tests for application restrictions persisting via profile owner: + * make -j FrameworksServicesTests + * runtest --path frameworks/base/services/tests/servicestests/ \ + * src/com/android/server/devicepolicy/ApplicationRestrictionsTest.java + */ +public class ApplicationRestrictionsTest extends AndroidTestCase { + + static DevicePolicyManager sDpm; + static ComponentName sAdminReceiver; + private static final String RESTRICTED_APP = "com.example.restrictedApp"; + static boolean sAddBack = false; + + public static class AdminReceiver extends DeviceAdminReceiver { + + @Override + public void onDisabled(Context context, Intent intent) { + if (sAddBack) { + sDpm.setActiveAdmin(sAdminReceiver, false); + sAddBack = false; + } + + super.onDisabled(context, intent); + } + } + + @Override + public void setUp() { + final Context context = getContext(); + sAdminReceiver = new ComponentName(mContext.getPackageName(), + AdminReceiver.class.getName()); + sDpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); + Settings.Secure.putInt(context.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0); + sDpm.setProfileOwner(context.getPackageName(), "Test", UserHandle.myUserId()); + Settings.Secure.putInt(context.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 1); + // Remove the admin if already registered. It's async, so add it back + // when the admin gets a broadcast. Otherwise add it back right away. + if (sDpm.isAdminActive(sAdminReceiver)) { + sAddBack = true; + sDpm.removeActiveAdmin(sAdminReceiver); + } else { + sDpm.setActiveAdmin(sAdminReceiver, false); + } + } + + @Override + public void tearDown() { + Settings.Secure.putInt(getContext().getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0); + sDpm.removeActiveAdmin(sAdminReceiver); + Settings.Secure.putInt(getContext().getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 1); + } + + public void testSettingRestrictions() { + Bundle restrictions = new Bundle(); + restrictions.putString("KEY_STRING", "Foo"); + assertNotNull(sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP)); + sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions); + Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP); + assertNotNull(returned); + assertEquals(returned.size(), 1); + assertEquals(returned.get("KEY_STRING"), "Foo"); + sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle()); + returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP); + assertEquals(returned.size(), 0); + } + + public void testRestrictionTypes() { + Bundle restrictions = new Bundle(); + restrictions.putString("KEY_STRING", "Foo"); + restrictions.putInt("KEY_INT", 7); + restrictions.putBoolean("KEY_BOOLEAN", true); + restrictions.putBoolean("KEY_BOOLEAN_2", false); + restrictions.putString("KEY_STRING_2", "Bar"); + restrictions.putStringArray("KEY_STR_ARRAY", new String[] { "Foo", "Bar" }); + sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions); + Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP); + assertTrue(returned.getBoolean("KEY_BOOLEAN")); + assertFalse(returned.getBoolean("KEY_BOOLEAN_2")); + assertFalse(returned.getBoolean("KEY_BOOLEAN_3")); + assertEquals(returned.getInt("KEY_INT"), 7); + assertTrue(returned.get("KEY_BOOLEAN") instanceof Boolean); + assertTrue(returned.get("KEY_INT") instanceof Integer); + assertEquals(returned.get("KEY_STRING"), "Foo"); + assertEquals(returned.get("KEY_STRING_2"), "Bar"); + assertTrue(returned.getStringArray("KEY_STR_ARRAY") instanceof String[]); + sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, new Bundle()); + } + + public void testTextEscaping() { + String fancyText = "<This contains XML/> <JSON> " + + "{ \"One\": { \"OneOne\": \"11\", \"OneTwo\": \"12\" }, \"Two\": \"2\" } <JSON/>"; + Bundle restrictions = new Bundle(); + restrictions.putString("KEY_FANCY_TEXT", fancyText); + sDpm.setApplicationRestrictions(sAdminReceiver, RESTRICTED_APP, restrictions); + Bundle returned = sDpm.getApplicationRestrictions(sAdminReceiver, RESTRICTED_APP); + assertEquals(returned.getString("KEY_FANCY_TEXT"), fancyText); + } +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java new file mode 100644 index 0000000..a6fdee9 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.notification; + +import android.app.Notification; +import android.os.Bundle; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.text.SpannableString; + +import java.util.ArrayList; +import java.util.Arrays; + +public class ValidateNotificationPeopleTest extends AndroidTestCase { + + @SmallTest + public void testNoExtra() throws Exception { + Bundle bundle = new Bundle(); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertNull("lack of extra should return null", result); + } + + @SmallTest + public void testSingleString() throws Exception { + String[] expected = { "foobar" }; + Bundle bundle = new Bundle(); + bundle.putString(Notification.EXTRA_PEOPLE, expected[0]); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("string should be in result[0]", expected, result); + } + + @SmallTest + public void testSingleCharArray() throws Exception { + String[] expected = { "foobar" }; + Bundle bundle = new Bundle(); + bundle.putCharArray(Notification.EXTRA_PEOPLE, expected[0].toCharArray()); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("char[] should be in result[0]", expected, result); + } + + @SmallTest + public void testSingleCharSequence() throws Exception { + String[] expected = { "foobar" }; + Bundle bundle = new Bundle(); + bundle.putCharSequence(Notification.EXTRA_PEOPLE, new SpannableString(expected[0])); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("charSequence should be in result[0]", expected, result); + } + + @SmallTest + public void testStringArraySingle() throws Exception { + Bundle bundle = new Bundle(); + String[] expected = { "foobar" }; + bundle.putStringArray(Notification.EXTRA_PEOPLE, expected); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("wrapped string should be in result[0]", expected, result); + } + + @SmallTest + public void testStringArrayMultiple() throws Exception { + Bundle bundle = new Bundle(); + String[] expected = { "foo", "bar", "baz" }; + bundle.putStringArray(Notification.EXTRA_PEOPLE, expected); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("testStringArrayMultiple", expected, result); + } + + @SmallTest + public void testStringArrayNulls() throws Exception { + Bundle bundle = new Bundle(); + String[] expected = { "foo", null, "baz" }; + bundle.putStringArray(Notification.EXTRA_PEOPLE, expected); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("testStringArrayNulls", expected, result); + } + + @SmallTest + public void testCharSequenceArrayMultiple() throws Exception { + Bundle bundle = new Bundle(); + String[] expected = { "foo", "bar", "baz" }; + CharSequence[] charSeqArray = new CharSequence[expected.length]; + for (int i = 0; i < expected.length; i++) { + charSeqArray[i] = new SpannableString(expected[i]); + } + bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE, charSeqArray); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("testCharSequenceArrayMultiple", expected, result); + } + + @SmallTest + public void testMixedCharSequenceArrayList() throws Exception { + Bundle bundle = new Bundle(); + String[] expected = { "foo", "bar", "baz" }; + CharSequence[] charSeqArray = new CharSequence[expected.length]; + for (int i = 0; i < expected.length; i++) { + if (i % 2 == 0) { + charSeqArray[i] = expected[i]; + } else { + charSeqArray[i] = new SpannableString(expected[i]); + } + } + bundle.putCharSequenceArray(Notification.EXTRA_PEOPLE, charSeqArray); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("testMixedCharSequenceArrayList", expected, result); + } + + @SmallTest + public void testStringArrayList() throws Exception { + Bundle bundle = new Bundle(); + String[] expected = { "foo", null, "baz" }; + final ArrayList<String> stringArrayList = new ArrayList<String>(expected.length); + for (int i = 0; i < expected.length; i++) { + stringArrayList.add(expected[i]); + } + bundle.putStringArrayList(Notification.EXTRA_PEOPLE, stringArrayList); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("testStringArrayList", expected, result); + } + + @SmallTest + public void testCharSequenceArrayList() throws Exception { + Bundle bundle = new Bundle(); + String[] expected = { "foo", "bar", "baz" }; + final ArrayList<CharSequence> stringArrayList = + new ArrayList<CharSequence>(expected.length); + for (int i = 0; i < expected.length; i++) { + stringArrayList.add(new SpannableString(expected[i])); + } + bundle.putCharSequenceArrayList(Notification.EXTRA_PEOPLE, stringArrayList); + String[] result = ValidateNotificationPeople.getExtraPeople(bundle); + assertStringArrayEquals("testCharSequenceArrayList", expected, result); + } + + private void assertStringArrayEquals(String message, String[] expected, String[] result) { + String expectedString = Arrays.toString(expected); + String resultString = Arrays.toString(result); + assertEquals(message + ": arrays differ", expectedString, resultString); + } +} diff --git a/telecomm/java/android/telecomm/package.html b/telecomm/java/android/telecomm/package.html deleted file mode 100644 index 1c9bf9d..0000000 --- a/telecomm/java/android/telecomm/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body> - {@hide} -</body> -</html> diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 525441d..ffa9a4e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1752,6 +1752,8 @@ public class TelephonyManager { * * @param AID Application id. See ETSI 102.221 and 101.220. * @return The logical channel id which is negative on error. + * + * @hide */ public int iccOpenLogicalChannel(String AID) { try { @@ -1773,6 +1775,8 @@ public class TelephonyManager { * @param channel is the channel id to be closed as retruned by a successful * iccOpenLogicalChannel. * @return true if the channel was closed successfully. + * + * @hide */ public boolean iccCloseLogicalChannel(int channel) { try { @@ -1802,6 +1806,8 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. If an error occurs, an empty string is returned. + * + * @hide */ public String iccTransmitApduLogicalChannel(int channel, int cla, int instruction, int p1, int p2, int p3, String data) { @@ -1826,6 +1832,8 @@ public class TelephonyManager { * @return The APDU response from the ICC card, with the last 4 bytes * being the status word. If the command fails, returns an empty * string. + * + * @hide */ public String sendEnvelopeWithStatus(String content) { try { diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 59bdf64..874279b 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -98,6 +98,7 @@ public class DctConstants { public static final int CMD_IS_PROVISIONING_APN = BASE + 38; public static final int EVENT_PROVISIONING_APN_ALARM = BASE + 39; public static final int CMD_NET_STAT_POLL = BASE + 40; + public static final int EVENT_DATA_RAT_CHANGED = BASE + 41; /***** Constants *****/ diff --git a/test-runner/src/android/test/MoreAsserts.java b/test-runner/src/android/test/MoreAsserts.java index fb0faba..3364895 100644 --- a/test-runner/src/android/test/MoreAsserts.java +++ b/test-runner/src/android/test/MoreAsserts.java @@ -128,6 +128,33 @@ public final class MoreAsserts { } /** + * @hide Asserts that array {@code actual} is the same size and every element equals + * those in array {@code expected}. On failure, message indicates first + * specific element mismatch. + */ + public static void assertEquals( + String message, long[] expected, long[] actual) { + if (expected.length != actual.length) { + failWrongLength(message, expected.length, actual.length); + } + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual[i]) { + failWrongElement(message, i, expected[i], actual[i]); + } + } + } + + /** + * @hide Asserts that array {@code actual} is the same size and every element equals + * those in array {@code expected}. On failure, message indicates first + * specific element mismatch. + */ + public static void assertEquals(long[] expected, long[] actual) { + assertEquals(null, expected, actual); + } + + + /** * Asserts that array {@code actual} is the same size and every element equals * those in array {@code expected}. On failure, message indicates first * specific element mismatch. diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 3190fb0..17db1b4 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -707,8 +707,8 @@ public class MockPackageManager extends PackageManager { * @hide */ @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int userIdOrig, - int userIdDest) { + public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, + int sourceUserId, int targetUserId) { throw new UnsupportedOperationException(); } @@ -716,7 +716,24 @@ public class MockPackageManager extends PackageManager { * @hide */ @Override - public void clearForwardingIntentFilters(int userIdOrig) { + public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId, + int targetUserId) { + throw new UnsupportedOperationException(); + } + + /** + * @hide + */ + @Override + public void clearCrossProfileIntentFilters(int sourceUserId) { + throw new UnsupportedOperationException(); + } + + /** + * @hide + */ + @Override + public void clearForwardingIntentFilters(int sourceUserId) { throw new UnsupportedOperationException(); } diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java index c7715ad..fc5426c 100644 --- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java +++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java @@ -43,7 +43,6 @@ public class MainActivity extends Activity implements OnItemClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - HardwareRenderer.sUseRenderThread = true; setContentView(R.layout.activity_main); ListView lv = (ListView) findViewById(android.R.id.list); lv.setDrawSelectorOnTop(true); diff --git a/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java new file mode 100644 index 0000000..31484f4 --- /dev/null +++ b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsSemioticClassTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.speech.tts; + +import android.test.InstrumentationTestCase; + +import android.speech.tts.Markup; +import android.speech.tts.Utterance; +import android.speech.tts.Utterance.AbstractTtsSemioticClass; + +public class AbstractTtsSemioticClassTest extends InstrumentationTestCase { + + public static class TtsMock extends AbstractTtsSemioticClass<TtsMock> { + public TtsMock() { + super(); + } + + public TtsMock(Markup markup) { + super(); + } + + public void setType(String type) { + mMarkup.setType(type); + } + } + + public void testFluentAPI() { + new TtsMock() + .setPlainText("a plaintext") // from AbstractTts + .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass + .setType("test"); // from TtsMock + } + + public void testDefaultConstructor() { + new TtsMock(); + } + + public void testMarkupConstructor() { + Markup markup = new Markup(); + new TtsMock(markup); + } + + public void testGetType() { + TtsMock t = new TtsMock(); + t.setType("type1"); + assertEquals("type1", t.getType()); + t.setType(null); + assertEquals(null, t.getType()); + t.setType("type2"); + assertEquals("type2", t.getType()); + } + + + public void testDefaultGender() { + assertEquals(Utterance.GENDER_UNKNOWN, new TtsMock().getGender()); + } + + public void testSetGender() { + assertEquals(Utterance.GENDER_MALE, + new TtsMock().setGender(Utterance.GENDER_MALE).getGender()); + } + + public void testSetGenderNegative() { + try { + new TtsMock().setGender(-1); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testSetGenderOutOfBounds() { + try { + new TtsMock().setGender(4); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testDefaultAnimacy() { + assertEquals(Utterance.ANIMACY_UNKNOWN, new TtsMock().getAnimacy()); + } + + public void testSetAnimacy() { + assertEquals(Utterance.ANIMACY_ANIMATE, + new TtsMock().setAnimacy(Utterance.ANIMACY_ANIMATE).getAnimacy()); + } + + public void testSetAnimacyNegative() { + try { + new TtsMock().setAnimacy(-1); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testSetAnimacyOutOfBounds() { + try { + new TtsMock().setAnimacy(4); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testDefaultMultiplicity() { + assertEquals(Utterance.MULTIPLICITY_UNKNOWN, new TtsMock().getMultiplicity()); + } + + public void testSetMultiplicity() { + assertEquals(Utterance.MULTIPLICITY_DUAL, + new TtsMock().setMultiplicity(Utterance.MULTIPLICITY_DUAL).getMultiplicity()); + } + + public void testSetMultiplicityNegative() { + try { + new TtsMock().setMultiplicity(-1); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testSetMultiplicityOutOfBounds() { + try { + new TtsMock().setMultiplicity(4); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testDefaultCase() { + assertEquals(Utterance.CASE_UNKNOWN, new TtsMock().getCase()); + } + + public void testSetCase() { + assertEquals(Utterance.CASE_VOCATIVE, + new TtsMock().setCase(Utterance.CASE_VOCATIVE).getCase()); + } + + public void testSetCaseNegative() { + try { + new TtsMock().setCase(-1); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testSetCaseOutOfBounds() { + try { + new TtsMock().setCase(9); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testToString() { + TtsMock t = new TtsMock() + .setAnimacy(Utterance.ANIMACY_INANIMATE) + .setCase(Utterance.CASE_INSTRUMENTAL) + .setGender(Utterance.GENDER_FEMALE) + .setMultiplicity(Utterance.MULTIPLICITY_PLURAL); + String str = + "animacy: \"2\" " + + "case: \"8\" " + + "gender: \"3\" " + + "multiplicity: \"3\""; + assertEquals(str, t.toString()); + } + + public void testToStringSetToUnkown() { + TtsMock t = new TtsMock() + .setAnimacy(Utterance.ANIMACY_INANIMATE) + .setCase(Utterance.CASE_INSTRUMENTAL) + .setGender(Utterance.GENDER_FEMALE) + .setMultiplicity(Utterance.MULTIPLICITY_PLURAL) + // set back to unknown + .setAnimacy(Utterance.ANIMACY_UNKNOWN) + .setCase(Utterance.CASE_UNKNOWN) + .setGender(Utterance.GENDER_UNKNOWN) + .setMultiplicity(Utterance.MULTIPLICITY_UNKNOWN); + String str = ""; + assertEquals(str, t.toString()); + } + +} diff --git a/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java new file mode 100644 index 0000000..281c97f --- /dev/null +++ b/tests/TtsTests/src/com/android/speech/tts/AbstractTtsTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.speech.tts; + +import android.test.InstrumentationTestCase; + +import android.speech.tts.Markup; +import android.speech.tts.Utterance.AbstractTts; + +public class AbstractTtsTest extends InstrumentationTestCase { + + public static class TtsMock extends AbstractTts<TtsMock> { + public TtsMock() { + super(); + } + + public TtsMock(Markup markup) { + super(); + } + + public void setType(String type) { + mMarkup.setType(type); + } + + @Override + public TtsMock setParameter(String key, String value) { + return super.setParameter(key, value); + } + + @Override + public TtsMock removeParameter(String key) { + return super.removeParameter(key); + } + } + + public void testDefaultConstructor() { + new TtsMock(); + } + + public void testMarkupConstructor() { + Markup markup = new Markup(); + new TtsMock(markup); + } + + public void testGetType() { + TtsMock t = new TtsMock(); + t.setType("type1"); + assertEquals("type1", t.getType()); + t.setType(null); + assertEquals(null, t.getType()); + t.setType("type2"); + assertEquals("type2", t.getType()); + } + + public void testGeneratePlainText() { + assertNull(new TtsMock().generatePlainText()); + } + + public void testToString() { + TtsMock t = new TtsMock(); + t.setType("a_type"); + t.setPlainText("a plaintext"); + t.setParameter("key1", "value1"); + t.setParameter("aaa", "value2"); + String str = + "type: \"a_type\" " + + "plain_text: \"a plaintext\" " + + "aaa: \"value2\" " + + "key1: \"value1\""; + assertEquals(str, t.toString()); + } + + public void testRemoveParameter() { + TtsMock t = new TtsMock(); + t.setParameter("key1", "value 1"); + t.setParameter("aaa", "value a"); + t.removeParameter("key1"); + String str = + "aaa: \"value a\""; + assertEquals(str, t.toString()); + } + + public void testRemoveParameterBySettingNull() { + TtsMock t = new TtsMock(); + t.setParameter("key1", "value 1"); + t.setParameter("aaa", "value a"); + t.setParameter("aaa", null); + String str = + "key1: \"value 1\""; + assertEquals(str, t.toString()); + } +} diff --git a/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java b/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java new file mode 100644 index 0000000..7ef93ce --- /dev/null +++ b/tests/TtsTests/src/com/android/speech/tts/MarkupTest.java @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.speech.tts; + +import junit.framework.Assert; +import android.os.Parcel; +import android.test.InstrumentationTestCase; + +import android.speech.tts.Markup; + +public class MarkupTest extends InstrumentationTestCase { + + public void testEmptyMarkup() { + Markup markup = new Markup(); + assertNull(markup.getType()); + assertNull(markup.getPlainText()); + assertEquals(0, markup.parametersSize()); + assertEquals(0, markup.nestedMarkupSize()); + } + + public void testGetSetType() { + Markup markup = new Markup(); + markup.setType("one"); + assertEquals("one", markup.getType()); + markup.setType(null); + assertNull(markup.getType()); + markup.setType("two"); + assertEquals("two", markup.getType()); + } + + public void testGetSetPlainText() { + Markup markup = new Markup(); + markup.setPlainText("one"); + assertEquals("one", markup.getPlainText()); + markup.setPlainText(null); + assertNull(markup.getPlainText()); + markup.setPlainText("two"); + assertEquals("two", markup.getPlainText()); + } + + public void testParametersSize1() { + Markup markup = new Markup(); + markup.addNestedMarkup(new Markup()); + assertEquals(1, markup.nestedMarkupSize()); + } + + public void testParametersSize2() { + Markup markup = new Markup(); + markup.addNestedMarkup(new Markup()); + markup.addNestedMarkup(new Markup()); + assertEquals(2, markup.nestedMarkupSize()); + } + + public void testRemoveParameter() { + Markup m = new Markup("type"); + m.setParameter("key1", "value1"); + m.setParameter("key2", "value2"); + m.setParameter("key3", "value3"); + assertEquals(3, m.parametersSize()); + m.removeParameter("key1"); + assertEquals(2, m.parametersSize()); + m.removeParameter("key3"); + assertEquals(1, m.parametersSize()); + assertNull(m.getParameter("key1")); + assertEquals("value2", m.getParameter("key2")); + assertNull(m.getParameter("key3")); + } + + public void testEmptyEqual() { + Markup m1 = new Markup(); + Markup m2 = new Markup(); + assertTrue(m1.equals(m2)); + } + + public void testFilledEqual() { + Markup m1 = new Markup(); + m1.setType("type"); + m1.setPlainText("plain text"); + m1.setParameter("key1", "value1"); + m1.addNestedMarkup(new Markup()); + Markup m2 = new Markup(); + m2.setType("type"); + m2.setPlainText("plain text"); + m2.setParameter("key1", "value1"); + m2.addNestedMarkup(new Markup()); + assertTrue(m1.equals(m2)); + } + + public void testDifferentTypeEqual() { + Markup m1 = new Markup(); + m1.setType("type1"); + Markup m2 = new Markup(); + m2.setType("type2"); + assertFalse(m1.equals(m2)); + } + + public void testDifferentPlainTextEqual() { + Markup m1 = new Markup(); + m1.setPlainText("plainText1"); + Markup m2 = new Markup(); + m2.setPlainText("plainText2"); + assertFalse(m1.equals(m2)); + } + + public void testDifferentParamEqual() { + Markup m1 = new Markup(); + m1.setParameter("test", "value1"); + Markup m2 = new Markup(); + m2.setParameter("test", "value2"); + assertFalse(m1.equals(m2)); + } + + public void testDifferentParameterKeyEqual() { + Markup m1 = new Markup(); + m1.setParameter("test1", "value"); + Markup m2 = new Markup(); + m2.setParameter("test2", "value"); + assertFalse(m1.equals(m2)); + } + + public void testDifferentParameterValueEqual() { + Markup m1 = new Markup(); + m1.setParameter("test", "value1"); + Markup m2 = new Markup(); + m2.setParameter("test", "value2"); + assertFalse(m1.equals(m2)); + } + + public void testDifferentNestedMarkupEqual() { + Markup m1 = new Markup(); + Markup nested = new Markup(); + nested.setParameter("key", "value"); + m1.addNestedMarkup(nested); + Markup m2 = new Markup(); + m2.addNestedMarkup(new Markup()); + assertFalse(m1.equals(m2)); + } + + public void testEmptyToFromString() { + Markup m1 = new Markup(); + String str = m1.toString(); + assertEquals("", str); + + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + public void testTypeToFromString() { + Markup m1 = new Markup("atype"); + String str = m1.toString(); + assertEquals("type: \"atype\"", str); + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + public void testPlainTextToFromString() { + Markup m1 = new Markup(); + m1.setPlainText("some_plainText"); + String str = m1.toString(); + assertEquals("plain_text: \"some_plainText\"", str); + + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + public void testParameterToFromString() { + Markup m1 = new Markup("cardinal"); + m1.setParameter("integer", "-22"); + String str = m1.toString(); + assertEquals("type: \"cardinal\" integer: \"-22\"", str); + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + // Parameters should be ordered alphabettically, so the output is stable. + public void testParameterOrderToFromString() { + Markup m1 = new Markup("cardinal"); + m1.setParameter("ccc", "-"); + m1.setParameter("aaa", "-"); + m1.setParameter("aa", "-"); + m1.setParameter("bbb", "-"); + String str = m1.toString(); + assertEquals( + "type: \"cardinal\" " + + "aa: \"-\" " + + "aaa: \"-\" " + + "bbb: \"-\" " + + "ccc: \"-\"", + str); + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + public void testEmptyNestedToFromString() { + Markup m1 = new Markup("atype"); + m1.addNestedMarkup(new Markup()); + String str = m1.toString(); + assertEquals("type: \"atype\" markup {}", str); + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + public void testNestedWithTypeToFromString() { + Markup m1 = new Markup("atype"); + m1.addNestedMarkup(new Markup("nested_type")); + String str = m1.toString(); + assertEquals( + "type: \"atype\" " + + "markup { type: \"nested_type\" }", + str); + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + public void testRemoveNestedMarkup() { + Markup m = new Markup("atype"); + Markup m1 = new Markup("nested_type1"); + Markup m2 = new Markup("nested_type2"); + Markup m3 = new Markup("nested_type3"); + m.addNestedMarkup(m1); + m.addNestedMarkup(m2); + m.addNestedMarkup(m3); + m.removeNestedMarkup(m1); + m.removeNestedMarkup(m3); + String str = m.toString(); + assertEquals( + "type: \"atype\" " + + "markup { type: \"nested_type2\" }", + str); + Markup mFromString = Markup.markupFromString(str); + assertEquals(m, mFromString); + } + + public void testLotsofNestingToFromString() { + Markup m1 = new Markup("top") + .addNestedMarkup(new Markup("top_child1") + .addNestedMarkup(new Markup("top_child1_child1")) + .addNestedMarkup(new Markup("top_child1_child2"))) + .addNestedMarkup(new Markup("top_child2") + .addNestedMarkup(new Markup("top_child2_child2")) + .addNestedMarkup(new Markup("top_child2_child2"))); + + String str = m1.toString(); + assertEquals( + "type: \"top\" " + + "markup { " + + "type: \"top_child1\" " + + "markup { type: \"top_child1_child1\" } " + + "markup { type: \"top_child1_child2\" } " + + "} " + + "markup { " + + "type: \"top_child2\" " + + "markup { type: \"top_child2_child2\" } " + + "markup { type: \"top_child2_child2\" } " + + "}", + str); + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + public void testFilledToFromString() { + Markup m1 = new Markup("measure"); + m1.setPlainText("fifty-five amps"); + m1.setParameter("unit", "meter"); + m1.addNestedMarkup(new Markup("cardinal").setParameter("integer", "55")); + String str = m1.toString(); + assertEquals( + "type: \"measure\" " + + "plain_text: \"fifty-five amps\" " + + "unit: \"meter\" " + + "markup { type: \"cardinal\" integer: \"55\" }", + str); + + Markup m2 = Markup.markupFromString(str); + assertEquals(m1, m2); + } + + public void testErrorFromString() { + String str = "type: \"atype\" markup {mistake}"; + try { + Markup.markupFromString(str); + Assert.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testEscapeQuotes() { + Markup m1 = new Markup("text") + .setParameter("something_unknown", "\"this\" is \"a sentence \" with quotes\""); + String str = m1.toString(); + assertEquals( + "type: \"text\" " + + "something_unknown: \"\\\"this\\\" is \\\"a sentence \\\" with quotes\\\"\"", + str); + + Markup m2 = Markup.markupFromString(str); + assertEquals(m1.toString(), m2.toString()); + assertEquals(m1, m2); + } + + public void testEscapeSlashes1() { + Markup m1 = new Markup("text") + .setParameter("something_unknown", "\\ \\\\ \t \n \""); + String str = m1.toString(); + assertEquals( + "type: \"text\" " + + "something_unknown: \"\\\\ \\\\\\\\ \t \n \\\"\"", + str); + + Markup m2 = Markup.markupFromString(str); + assertEquals(m1.toString(), m2.toString()); + assertEquals(m1, m2); + } + + public void testEscapeSlashes2() { + Markup m1 = new Markup("text") + .setParameter("something_unknown", "\\\"\\\"\\\\\"\"\\\\\\\"\"\""); + String str = m1.toString(); + assertEquals( + "type: \"text\" " + + "something_unknown: \"\\\\\\\"\\\\\\\"\\\\\\\\\\\"\\\"\\\\\\\\\\\\\\\"\\\"\\\"\"", + str); + + Markup m2 = Markup.markupFromString(str); + assertEquals(m1.toString(), m2.toString()); + assertEquals(m1, m2); + } + + public void testBadInput1() { + String str = "type: \"text\" text: \"\\\""; + try { + Markup.markupFromString(str); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testBadInput2() { + String str = "type: \"text\" text: \"\\a\""; + try { + Markup.markupFromString(str); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testValidParameterKey() { + Markup m = new Markup(); + m.setParameter("ke9__yk_88ey_za7_", "test"); + } + + public void testInValidParameterKeyEmpty() { + Markup m = new Markup(); + try { + m.setParameter("", "test"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testInValidParameterKeyDollar() { + Markup m = new Markup(); + try { + m.setParameter("ke9y$k88ey7", "test"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testInValidParameterKeySpace() { + Markup m = new Markup(); + try { + m.setParameter("ke9yk88ey7 ", "test"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testValidType() { + new Markup("_this_is_1_valid_type_222"); + } + + public void testInValidTypeAmpersand() { + try { + new Markup("abcde1234&"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testInValidTypeSpace() { + try { + new Markup(" "); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testSimpleParcelable() { + Markup markup = new Markup(); + + Parcel parcel = Parcel.obtain(); + markup.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel); + + assertFalse(markup == fromParcel); + assertEquals(markup, fromParcel); + } + + public void testTypeParcelable() { + Markup markup = new Markup("text"); + + Parcel parcel = Parcel.obtain(); + markup.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel); + + assertFalse(markup == fromParcel); + assertEquals(markup, fromParcel); + } + + public void testPlainTextsParcelable() { + Markup markup = new Markup(); + markup.setPlainText("plainText"); + + Parcel parcel = Parcel.obtain(); + markup.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel); + + assertFalse(markup == fromParcel); + assertEquals(markup, fromParcel); + } + + public void testParametersParcelable() { + Markup markup = new Markup(); + markup.setParameter("key1", "value1"); + markup.setParameter("key2", "value2"); + markup.setParameter("key3", "value3"); + + Parcel parcel = Parcel.obtain(); + markup.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel); + + assertFalse(markup == fromParcel); + assertEquals(markup, fromParcel); + } + + public void testNestedParcelable() { + Markup markup = new Markup(); + markup.addNestedMarkup(new Markup("first")); + markup.addNestedMarkup(new Markup("second")); + markup.addNestedMarkup(new Markup("third")); + + Parcel parcel = Parcel.obtain(); + markup.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel); + + assertFalse(markup == fromParcel); + assertEquals(markup, fromParcel); + } + + public void testAllFieldsParcelable() { + Markup markup = new Markup("text"); + markup.setPlainText("plain text"); + markup.setParameter("key1", "value1"); + markup.setParameter("key2", "value2"); + markup.setParameter("key3", "value3"); + markup.addNestedMarkup(new Markup("first")); + markup.addNestedMarkup(new Markup("second")); + markup.addNestedMarkup(new Markup("third")); + + Parcel parcel = Parcel.obtain(); + markup.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + Markup fromParcel = (Markup) Markup.CREATOR.createFromParcel(parcel); + + assertFalse(markup == fromParcel); + assertEquals(markup, fromParcel); + } + + public void testKeyCannotBeType() { + try { + new Markup().setParameter("type", "vale"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testKeyCannotBePlainText() { + try { + new Markup().setParameter("plain_text", "value"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } +} diff --git a/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java b/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java new file mode 100644 index 0000000..c34f4ac --- /dev/null +++ b/tests/TtsTests/src/com/android/speech/tts/TtsCardinalTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.speech.tts; + +import junit.framework.Assert; +import android.test.InstrumentationTestCase; +import android.test.MoreAsserts; + +import android.speech.tts.Markup; +import android.speech.tts.Utterance; +import android.speech.tts.Utterance.TtsCardinal; +import android.speech.tts.Utterance.TtsText; + +public class TtsCardinalTest extends InstrumentationTestCase { + + public void testConstruct() { + assertNotNull(new TtsCardinal(0)); + } + + public void testFluentAPI() { + new TtsCardinal() + .setPlainText("a plaintext") // from AbstractTts + .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass + .setInteger("-10001"); // from TtsText + } + + public void testZero() { + assertEquals("0", new TtsCardinal(0).getInteger()); + } + + public void testThirtyOne() { + assertEquals("31", new TtsCardinal(31).getInteger()); + } + + public void testMarkupZero() { + TtsCardinal c = new TtsCardinal(0); + Markup m = c.getMarkup(); + assertEquals("0", m.getParameter("integer")); + } + + public void testMarkupThirtyOne() { + TtsCardinal c = new TtsCardinal(31); + Markup m = c.getMarkup(); + assertEquals("31", m.getParameter("integer")); + } + + public void testMarkupThirtyOneString() { + TtsCardinal c = new TtsCardinal("31"); + Markup m = c.getMarkup(); + assertEquals("31", m.getParameter("integer")); + } + + public void testMarkupNegativeThirtyOne() { + TtsCardinal c = new TtsCardinal(-31); + Markup m = c.getMarkup(); + assertEquals("-31", m.getParameter("integer")); + } + + public void testMarkupMinusZero() { + TtsCardinal c = new TtsCardinal("-0"); + Markup m = c.getMarkup(); + assertEquals("-0", m.getParameter("integer")); + } + + public void testMarkupNegativeThirtyOneString() { + TtsCardinal c = new TtsCardinal("-31"); + Markup m = c.getMarkup(); + assertEquals("-31", m.getParameter("integer")); + } + + public void testOnlyLetters() { + try { + new TtsCardinal("abc"); + Assert.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testOnlyMinus() { + try { + new TtsCardinal("-"); + Assert.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testNegativeLetters() { + try { + new TtsCardinal("-abc"); + Assert.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testLetterNumberMix() { + try { + new TtsCardinal("-0a1b2c"); + Assert.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void letterNumberMix2() { + try { + new TtsCardinal("-a0b1c2"); + Assert.fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } +} diff --git a/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java b/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java new file mode 100644 index 0000000..35fd453 --- /dev/null +++ b/tests/TtsTests/src/com/android/speech/tts/TtsTextTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.speech.tts; + +import android.test.InstrumentationTestCase; + +import android.speech.tts.Markup; +import android.speech.tts.Utterance; +import android.speech.tts.Utterance.TtsText; + +public class TtsTextTest extends InstrumentationTestCase { + + public void testConstruct() { + assertNotNull(new TtsText()); + } + + public void testFluentAPI() { + new TtsText() + .setPlainText("a plaintext") // from AbstractTts + .setGender(Utterance.GENDER_MALE) // from AbstractTtsSemioticClass + .setText("text"); // from TtsText + } + + public void testConstructEmptyString() { + assertTrue(new TtsText("").getText().isEmpty()); + } + + public void testConstructString() { + assertEquals("this is a test.", new TtsText("this is a test.").getText()); + } + + public void testSetText() { + assertEquals("This is a test.", new TtsText().setText("This is a test.").getText()); + } + + public void testEmptyMarkup() { + TtsText t = new TtsText(); + Markup m = t.getMarkup(); + assertEquals("text", m.getType()); + assertNull(m.getPlainText()); + assertEquals(0, m.nestedMarkupSize()); + } + + public void testConstructStringMarkup() { + TtsText t = new TtsText("test"); + Markup m = t.getMarkup(); + assertEquals("text", m.getType()); + assertEquals("test", m.getParameter("text")); + assertEquals(0, m.nestedMarkupSize()); + } + + public void testSetStringMarkup() { + TtsText t = new TtsText(); + t.setText("test"); + Markup m = t.getMarkup(); + assertEquals("text", m.getType()); + assertEquals("test", m.getParameter("text")); + assertEquals(0, m.nestedMarkupSize()); + } +} diff --git a/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java b/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java new file mode 100644 index 0000000..8014dd1 --- /dev/null +++ b/tests/TtsTests/src/com/android/speech/tts/UtteranceTest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.speech.tts; + +import android.speech.tts.Markup; +import android.speech.tts.Utterance; +import android.speech.tts.Utterance.TtsCardinal; +import android.speech.tts.Utterance.TtsText; + +import android.test.InstrumentationTestCase; + +public class UtteranceTest extends InstrumentationTestCase { + + public void testEmptyUtterance() { + Utterance utt = new Utterance(); + assertEquals(0, utt.size()); + } + + public void testSizeCardinal() { + Utterance utt = new Utterance() + .append(new TtsCardinal(42)); + assertEquals(1, utt.size()); + } + + public void testSizeCardinalString() { + Utterance utt = new Utterance() + .append(new TtsCardinal(42)) + .append(new TtsText("is the answer")); + assertEquals(2, utt.size()); + } + + public void testMarkupEmpty() { + Markup m = new Utterance().createMarkup(); + assertEquals("utterance", m.getType()); + assertEquals("", m.getPlainText()); + } + + public void testMarkupCardinal() { + Utterance utt = new Utterance() + .append(new TtsCardinal(42)); + Markup markup = utt.createMarkup(); + assertEquals("utterance", markup.getType()); + assertEquals("42", markup.getPlainText()); + assertEquals("42", markup.getNestedMarkup(0).getParameter("integer")); + assertEquals("42", markup.getNestedMarkup(0).getPlainText()); + } + + public void testMarkupCardinalString() { + Utterance utt = new Utterance() + .append(new TtsCardinal(42)) + .append(new TtsText("is not just a number.")); + Markup markup = utt.createMarkup(); + assertEquals("utterance", markup.getType()); + assertEquals("42 is not just a number.", markup.getPlainText()); + assertEquals("cardinal", markup.getNestedMarkup(0).getType()); + assertEquals("42", markup.getNestedMarkup(0).getParameter("integer")); + assertEquals("42", markup.getNestedMarkup(0).getPlainText()); + assertEquals("text", markup.getNestedMarkup(1).getType()); + assertEquals("is not just a number.", markup.getNestedMarkup(1).getParameter("text")); + assertEquals("is not just a number.", markup.getNestedMarkup(1).getPlainText()); + } + + public void testTextCardinalToFromString() { + Utterance utt = new Utterance() + .append(new TtsCardinal(55)) + .append(new TtsText("this is a text.")); + String str = utt.toString(); + assertEquals( + "type: \"utterance\" " + + "markup { " + + "type: \"cardinal\" " + + "integer: \"55\" " + + "} " + + "markup { " + + "type: \"text\" " + + "text: \"this is a text.\" " + + "}" + , str); + + Utterance utt_new = Utterance.utteranceFromString(str); + assertEquals(str, utt_new.toString()); + } + + public void testNotUtteranceFromString() { + String str = + "type: \"this_is_not_an_utterance\" " + + "markup { " + + "type: \"cardinal\" " + + "plain_text: \"55\" " + + "integer: \"55\" " + + "}"; + try { + Utterance.utteranceFromString(str); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) {} + } + + public void testFromMarkup() { + String markup_str = + "type: \"utterance\" " + + "markup { " + + "type: \"cardinal\" " + + "plain_text: \"55\" " + + "integer: \"55\" " + + "} " + + "markup { " + + "type: \"text\" " + + "plain_text: \"this is a text.\" " + + "text: \"this is a text.\" " + + "}"; + Utterance utt = Utterance.utteranceFromString(markup_str); + assertEquals(markup_str, utt.toString()); + } + + public void testsetPlainText() { + Utterance utt = new Utterance() + .append(new TtsCardinal(-100).setPlainText("minus one hundred")); + assertEquals("minus one hundred", utt.get(0).getPlainText()); + } + + public void testRemoveTextThroughSet() { + Utterance utt = new Utterance() + .append(new TtsText().setText("test").setText(null)); + assertNull(((TtsText) utt.get(0)).getText()); + } + + public void testUnknownNodeWithPlainText() { + String str = + "type: \"utterance\" " + + "markup { " + + "type: \"some_future_feature\" " + + "plain_text: \"biep bob bob\" " + + "bombom: \"lorum ipsum\" " + + "}"; + Utterance utt = Utterance.utteranceFromString(str); + assertNotNull(utt); + assertEquals("text", utt.get(0).getType()); + assertEquals("biep bob bob", ((TtsText) utt.get(0)).getText()); + } + + public void testUnknownNodeWithNoPlainTexts() { + String str = + "type: \"utterance\" " + + "markup { " + + "type: \"some_future_feature\" " + + "bombom: \"lorum ipsum\" " + + "markup { type: \"cardinal\" integer: \"10\" } " + + "markup { type: \"text\" text: \"pears\" } " + + "}"; + Utterance utt = Utterance.utteranceFromString(str); + assertEquals( + "type: \"utterance\" " + + "markup { type: \"cardinal\" integer: \"10\" } " + + "markup { type: \"text\" text: \"pears\" }", utt.toString()); + } + + public void testCreateWarningOnFallbackTrue() { + Utterance utt = new Utterance() + .append(new TtsText("test")) + .setNoWarningOnFallback(true); + assertEquals( + "type: \"utterance\" " + + "no_warning_on_fallback: \"true\" " + + "markup { " + + "type: \"text\" " + + "text: \"test\" " + + "}", utt.toString()); + } + + public void testCreateWarningOnFallbackFalse() { + Utterance utt = new Utterance() + .append(new TtsText("test")) + .setNoWarningOnFallback(false); + assertEquals( + "type: \"utterance\" " + + "no_warning_on_fallback: \"false\" " + + "markup { " + + "type: \"text\" " + + "text: \"test\" " + + "}", utt.toString()); + } + + public void testCreatePlainTexts() { + Utterance utt = new Utterance() + .append(new TtsText("test")) + .append(new TtsCardinal(-55)); + assertEquals( + "type: \"utterance\" " + + "plain_text: \"test -55\" " + + "markup { type: \"text\" plain_text: \"test\" text: \"test\" } " + + "markup { type: \"cardinal\" plain_text: \"-55\" integer: \"-55\" }", + utt.createMarkup().toString() + ); + } + + public void testDontOverwritePlainTexts() { + Utterance utt = new Utterance() + .append(new TtsText("test").setPlainText("else")) + .append(new TtsCardinal(-55).setPlainText("44")); + assertEquals( + "type: \"utterance\" " + + "plain_text: \"else 44\" " + + "markup { type: \"text\" plain_text: \"else\" text: \"test\" } " + + "markup { type: \"cardinal\" plain_text: \"44\" integer: \"-55\" }", + utt.createMarkup().toString() + ); + } + + public void test99BottlesOnWallMarkup() { + Utterance utt = new Utterance() + .append("there are") + .append(99) + .append("bottles on the wall."); + assertEquals( + "type: \"utterance\" " + + "plain_text: \"there are 99 bottles on the wall.\" " + + "markup { type: \"text\" plain_text: \"there are\" text: \"there are\" } " + + "markup { type: \"cardinal\" plain_text: \"99\" integer: \"99\" } " + + "markup { type: \"text\" plain_text: \"bottles on the wall.\" text: \"bottles on the wall.\" }", + utt.createMarkup().toString()); + assertEquals("99", utt.createMarkup().getNestedMarkup(1).getPlainText()); + Markup markup = new Markup(utt.createMarkup()); + assertEquals("99", markup.getNestedMarkup(1).getPlainText()); + } + + public void testWhat() { + Utterance utt = new Utterance() + .append("there are") + .append(99) + .append("bottles on the wall."); + Markup m = utt.createMarkup(); + m.getNestedMarkup(1).getPlainText().equals("99"); + } +} diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml index 118f258..66a9452 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml @@ -13,8 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:versionCode="1" > +<vector xmlns:android="http://schemas.android.com/apk/res/android"> <size android:height="48dp" @@ -24,12 +23,13 @@ android:viewportHeight="480" android:viewportWidth="480" /> - <path - android:name="box1" - android:fill="?android:attr/colorControlActivated" - android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z" - android:stroke="?android:attr/colorControlActivated" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - -</vector>
\ No newline at end of file + <group> + <path + android:name="box1" + android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z" + android:fill="?android:attr/colorControlActivated" + android:stroke="?android:attr/colorControlActivated" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + </group> +</vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml index 034f7a0..40f23f0 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml @@ -1,5 +1,4 @@ -<!-- - Copyright (C) 2014 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,23 +15,22 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:height="64dp" - android:width="64dp" /> + android:width="64dp" + android:height="64dp"/> - <viewport - android:viewportHeight="320" - android:viewportWidth="320" /> - - <path - android:name="house" - android:fill="#ff440000" - android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z" - android:pivotX="70" - android:pivotY="120" + <viewport android:viewportWidth="320" + android:viewportHeight="320"/> + <group android:rotation="180" - android:stroke="#FF00FF00" - android:strokeWidth="10" - android:trimPathEnd=".9" - android:trimPathStart=".1" /> - -</vector>
\ No newline at end of file + android:pivotX="70" + android:pivotY="120"> + <path + android:name="house" + android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z" + android:fill="#ff440000" + android:stroke="#FF00FF00" + android:strokeWidth="10" + android:trimPathStart=".1" + android:trimPathEnd=".9"/> + </group> +</vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml index 451b28e..5b4c4ab 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml @@ -23,40 +23,47 @@ android:viewportHeight="12.25" android:viewportWidth="7.30625" /> - <path - android:name="clip1" - android:clipToPath="true" - android:pathData=" - M 0, 0 - l 7.3, 0 - l 0, 0 - l -7.3, 0 - z" + <group android:pivotX="3.65" android:pivotY="6.125" - android:rotation="-30" /> - <path - android:name="one" - android:fill="#ff88ff" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 - l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 - l -5.046875,0.0 0.0,-1.0Z" /> - <path - android:name="clip2" - android:clipToPath="true" - android:pathData=" - M 0, 0 + android:rotation="-30" > + <path + android:name="clip1" + android:clipToPath="true" + android:pathData=" + M 0, 6.125 l 7.3, 0 l 0, 12.25 l -7.3, 0 - z" + z" /> + </group> + <group> + <path + android:name="one" + android:fill="#ff88ff" + android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 + l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 + l -5.046875,0.0 0.0,-1.0Z" /> + </group> + <group android:pivotX="3.65" android:pivotY="6.125" - android:rotation="-30" /> - <path - android:name="two" - android:fill="#ff88ff" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 + android:rotation="-30" > + <path + android:name="clip2" + android:clipToPath="true" + android:pathData=" + M 0, 0 + l 7.3, 0 + l 0, 6.125 + l -7.3, 0 + z" /> + </group> + <group> + <path + android:name="two" + android:fill="#ff88ff" + android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 @@ -65,5 +72,6 @@ q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 q -0.78125024,0.8125 -2.2187502,2.265625Z" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml index 6f9caa8..90694fb 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml @@ -1,5 +1,4 @@ -<!-- - Copyright (C) 2014 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,44 +12,48 @@ See the License for the specific language governing permissions and limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > +<vector xmlns:android="http://schemas.android.com/apk/res/android"> <size - android:height="64dp" - android:width="64dp" /> + android:width="64dp" + android:height="64dp"/> <viewport - android:viewportHeight="12.25" - android:viewportWidth="7.30625" /> + android:viewportWidth="7.30625" + android:viewportHeight="12.25"/> - <path - android:name="clip1" - android:clipToPath="true" - android:fill="#112233" - android:pathData=" + <group> + <path + android:name="clip1" + android:pathData=" M 3.65, 6.125 m -.001, 0 a .001,.001 0 1,0 .002,0 - a .001,.001 0 1,0 -.002,0z" /> - <path - android:name="one" - android:fill="#ff88ff" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 + a .001,.001 0 1,0 -.002,0z" + android:clipToPath="true" + android:fill="#112233" + /> + + <path + android:name="one" + android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 - l -5.046875,0.0 0.0,-1.0Z" /> - <path - android:name="clip2" - android:clipToPath="true" - android:fill="#112233" - android:pathData=" + l -5.046875,0.0 0.0,-1.0Z" + android:fill="#ff88ff" + /> + <path + android:name="clip2" + android:pathData=" M 3.65, 6.125 m -6, 0 a 6,6 0 1,0 12,0 - a 6,6 0 1,0 -12,0z" /> - <path - android:name="two" - android:fill="#ff88ff" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 + a 6,6 0 1,0 -12,0z" + android:clipToPath="true" + android:fill="#112233" + /> + <path + android:name="two" + android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 @@ -58,6 +61,8 @@ q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875 q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 - q -0.78125024,0.8125 -2.2187502,2.265625Z" /> - -</vector>
\ No newline at end of file + q -0.78125024,0.8125 -2.2187502,2.265625Z" + android:fill="#ff88ff" + /> + </group> +</vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml index e6c2557..c6595fa 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml @@ -23,17 +23,18 @@ android:viewportHeight="12.25" android:viewportWidth="7.30625" /> - <path - android:name="one" - android:fill="#ffff00" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 + <group> + <path + android:name="one" + android:fill="#ffff00" + android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 l -5.046875,0.0 0.0,-1.0Z" /> - <path - android:name="two" - android:fill="#ffff00" - android:fillOpacity="0" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 + <path + android:name="two" + android:fill="#ffff00" + android:fillOpacity="0" + android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 @@ -42,5 +43,5 @@ q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 q -0.78125024,0.8125 -2.2187502,2.265625Z" /> - + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml index 3f8cc09..850de28 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml @@ -1,5 +1,4 @@ -<!-- - Copyright (C) 2014 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,38 +15,34 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:height="64dp" - android:width="64dp" /> + android:width="64dp" + android:height="64dp"/> <viewport - android:viewportHeight="700" - android:viewportWidth="700" /> + android:viewportWidth="700" + android:viewportHeight="700"/> - <path - android:name="path2451" - android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z" - android:stroke="#FF000000" - android:strokeWidth="30.65500000000000" /> - <path - android:name="path2453" - android:pathData="M 365.015 311.066" - android:stroke="#FF000000" - android:strokeWidth="30.655000000000001" /> - <path - android:name="path2455" - android:fill="#FFFFFFFF" - android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928" - android:stroke="#FF000000" - android:strokeWidth="30.655000000000001" /> - <path - android:name="path2457" - android:pathData="M 170.515 451.566L 305.61 313.46" - android:stroke="#000000" - android:strokeWidth="30.655000000000001" /> - <path - android:name="path2459" - android:pathData="M 557.968 449.974L 426.515 315.375" - android:stroke="#000000" - android:strokeWidth="30.655000000000001" /> - -</vector>
\ No newline at end of file + <group> + <path android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z" + android:name="path2451" + android:stroke="#FF000000" + android:strokeWidth="30.65500000000000"/> + <path android:pathData="M 365.015 311.066" + android:name="path2453" + android:stroke="#FF000000" + android:strokeWidth="30.655000000000001"/> + <path android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928" + android:name="path2455" + android:stroke="#FF000000" + android:fill="#FFFFFFFF" + android:strokeWidth="30.655000000000001"/> + <path android:pathData="M 170.515 451.566L 305.61 313.46" + android:name="path2457" + android:stroke="#000000" + android:strokeWidth="30.655000000000001"/> + <path android:pathData="M 557.968 449.974L 426.515 315.375" + android:name="path2459" + android:stroke="#000000" + android:strokeWidth="30.655000000000001"/> + </group> +</vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml index 4db5090..7c7e679 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml @@ -1,5 +1,4 @@ -<!-- - Copyright (C) 2014 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,21 +12,21 @@ See the License for the specific language governing permissions and limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - +<vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:height="64dp" - android:width="64dp" /> + android:width="64dp" + android:height="64dp"/> - <viewport - android:viewportHeight="110" - android:viewportWidth="140" /> + <viewport android:viewportWidth="140" + android:viewportHeight="110"/> - <path - android:name="back" - android:fill="#ffffffff" - android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z + <group> + <path + android:name="back" + android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z M 27,50 l 97,0 0,10 -97,0 z - M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z" /> - -</vector>
\ No newline at end of file + M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z" + android:fill="#ffffffff" + /> + </group> +</vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml index 44ef979..59f7459 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml @@ -1,5 +1,4 @@ -<!-- - Copyright (C) 2014 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,18 +15,20 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:height="64dp" - android:width="64dp" /> + android:width="64dp" + android:height="64dp"/> - <viewport - android:viewportHeight="600" - android:viewportWidth="600" /> - <path - android:name="pie1" - android:fill="#ffffcc00" - android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z" - android:stroke="#FF00FF00" - android:strokeWidth="1" /> + <viewport android:viewportWidth="600" + android:viewportHeight="600"/> -</vector>
\ No newline at end of file + <group> + <path + android:name="pie1" + android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z" + android:fill="#ffffcc00" + android:stroke="#FF00FF00" + android:strokeWidth="1"/> + </group> + +</vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml index 248a143..c93c85f 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml @@ -23,12 +23,14 @@ android:viewportHeight="200" android:viewportWidth="200" /> - <path - android:name="house" - android:fill="#ffffffff" - android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z" + <group android:pivotX="100" android:pivotY="100" - android:rotation="90" /> + android:rotation="90"> + <path + android:name="house" + android:fill="#ffffffff" + android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z"/> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml index 56c2972..8484e9e 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml @@ -21,24 +21,26 @@ android:width="64dp" /> <viewport - android:viewportHeight="200" - android:viewportWidth="200" /> + android:viewportWidth="200" + android:viewportHeight="200"/> - <path - android:name="bar3" - android:fill="#FFFFFFFF" - android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" /> - <path - android:name="bar2" - android:fill="#FFFFFFFF" - android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" /> - <path - android:name="bar1" - android:fill="#FF555555" - android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" /> - <path - android:name="bar0" - android:fill="#FF555555" - android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" /> + <group> + <path + android:name="bar3" + android:fill="#FFFFFFFF" + android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" /> + <path + android:name="bar2" + android:fill="#FFFFFFFF" + android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" /> + <path + android:name="bar1" + android:fill="#FF555555" + android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" /> + <path + android:name="bar0" + android:fill="#FF555555" + android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml index 16d8b48..3422bbf 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml @@ -23,16 +23,17 @@ android:viewportHeight="80" android:viewportWidth="40" /> - <path - android:name="battery" - android:fill="#3388ff" - android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z" - android:rotation="0" - android:stroke="#ff8833" - android:strokeWidth="1" /> - <path - android:name="spark" - android:fill="#FFFF0000" - android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" /> + <group> + <path + android:name="battery" + android:fill="#3388ff" + android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z" + android:stroke="#ff8833" + android:strokeWidth="1" /> + <path + android:name="spark" + android:fill="#FFFF0000" + android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml index 0a0407d..d0edd8c 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml @@ -23,20 +23,19 @@ android:viewportHeight="600" android:viewportWidth="600" /> - <path - android:name="pie1" - android:pathData="M300,70 a230,230 0 1,0 1,0 z" - android:stroke="#FF00FF00" - android:strokeWidth="70" - android:trimPathEnd=".75" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="v" - android:fill="#FF00FF00" - android:pathData="M300,70 l 0,-70 70,70 -70,70z" - android:pivotX="300" - android:pivotY="300" - android:rotation="0" /> + <group> + <path + android:name="pie1" + android:pathData="M300,70 a230,230 0 1,0 1,0 z" + android:stroke="#FF00FF00" + android:strokeWidth="70" + android:trimPathEnd=".75" + android:trimPathOffset="0" + android:trimPathStart="0" /> + <path + android:name="v" + android:fill="#FF00FF00" + android:pathData="M300,70 l 0,-70 70,70 -70,70z"/> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml index 385b1e9..8c946df 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml @@ -23,20 +23,19 @@ android:viewportHeight="400" android:viewportWidth="600" /> - <path - android:name="pie1" - android:fill="#ffffffff" - android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z" - android:stroke="#FF00FF00" - android:strokeWidth="1" /> - <path - android:name="half" - android:fill="#FFFF0000" - android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z" - android:pivotX="300" - android:pivotY="200" - android:rotation="0" - android:stroke="#FF0000FF" - android:strokeWidth="5" /> + <group> + <path + android:name="pie1" + android:fill="#ffffffff" + android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z" + android:stroke="#FF00FF00" + android:strokeWidth="1" /> + <path + android:name="half" + android:fill="#FFFF0000" + android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z" + android:stroke="#FF0000FF" + android:strokeWidth="5" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml index b701b35..1abe1e1 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml @@ -23,17 +23,19 @@ android:viewportHeight="500" android:viewportWidth="800" /> - <path - android:name="pie2" - android:pathData="M200,350 l 50,-25 + <group + android:pivotX="90" + android:pivotY="100" + android:rotation="20"> + <path + android:name="pie2" + android:pathData="M200,350 l 50,-25 a25,12 -30 0,1 100,-50 l 50,-25 a25,25 -30 0,1 100,-50 l 50,-25 a25,37 -30 0,1 100,-50 l 50,-25 a25,50 -30 0,1 100,-50 l 50,-25" - android:pivotX="90" - android:pivotY="100" - android:rotation="20" - android:stroke="#FF00FF00" - android:strokeWidth="10" /> + android:stroke="#FF00FF00" + android:strokeWidth="10" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml index 8d773e1..b08e157 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml @@ -23,14 +23,16 @@ android:viewportHeight="400" android:viewportWidth="500" /> - <path - android:name="house" - android:fill="#ff440000" - android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" + <group android:pivotX="250" android:pivotY="200" - android:rotation="180" - android:stroke="#FFFF0000" - android:strokeWidth="10" /> + android:rotation="180"> + <path + android:name="house" + android:fill="#ff440000" + android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" + android:stroke="#FFFF0000" + android:strokeWidth="10" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml index 3b7926c..ae85d9b 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml @@ -23,13 +23,29 @@ android:viewportHeight="200" android:viewportWidth="200" /> - <path - android:name="house" - android:pathData="M 100,10 v 90 M 10,100 h 90" + <group> + <path + android:name="background1" + android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z" + android:fill="#FF000000"/> + <path + android:name="background2" + android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z" + android:fill="#FF000000"/> + </group> + <group android:pivotX="100" android:pivotY="100" - android:rotation="360" - android:stroke="#FF00FF00" - android:strokeWidth="10" /> + android:rotation="90" + android:scaleX="0.75" + android:scaleY="0.5" + android:translateX="0.0" + android:translateY="100.0"> + <path + android:name="twoLines" + android:pathData="M 100,10 v 90 M 10,100 h 90" + android:stroke="#FF00FF00" + android:strokeWidth="10" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml index 1ec72be..8e98d02 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml @@ -1,5 +1,4 @@ -<!-- - Copyright (C) 2014 The Android Open Source Project +<!-- Copyright (C) 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,20 +15,18 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:height="64dp" - android:width="64dp" /> + android:width="64dp" + android:height="64dp"/> - <viewport - android:viewportHeight="600" - android:viewportWidth="1200" /> + <viewport android:viewportWidth="1200" + android:viewportHeight="600"/> - <path - android:name="house" - android:pathData="M200,300 Q400,50 600,300 T1000,300" - android:pivotX="600" - android:pivotY="300" - android:rotation="360" - android:stroke="#FFFF0000" - android:strokeWidth="10" /> + <group> + <path + android:name="house" + android:pathData="M200,300 Q400,50 600,300 T1000,300" + android:stroke="#FFFF0000" + android:strokeWidth="10"/> + </group> -</vector>
\ No newline at end of file +</vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml index 12d0e93..6d74ebd 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml @@ -23,13 +23,12 @@ android:viewportHeight="400" android:viewportWidth="500" /> - <path - android:name="house" - android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" - android:pivotX="250" - android:pivotY="200" - android:rotation="360" - android:stroke="#FFFFFF00" - android:strokeWidth="10" /> + <group> + <path + android:name="house" + android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" + android:stroke="#FFFFFF00" + android:strokeWidth="10" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml index 017e04c..a890fd6 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml @@ -23,12 +23,14 @@ android:viewportHeight="800" android:viewportWidth="1000" /> - <path - android:name="house" - android:pathData="M10,300 Q400,550 600,300 T1000,300" - android:pivotX="90" - android:pivotY="100" - android:stroke="#FFFF0000" - android:strokeWidth="60" /> + <group> + <path + android:name="house" + android:pathData="M10,300 Q400,550 600,300 T1000,300" + android:pivotX="90" + android:pivotY="100" + android:stroke="#FFFF0000" + android:strokeWidth="60" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml index b7002a3..b8af7e2 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml @@ -23,14 +23,16 @@ android:viewportHeight="480" android:viewportWidth="480" /> - <path - android:name="edit" - android:fill="#FF00FFFF" - android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333 + <group> + <path + android:name="edit" + android:fill="#FF00FFFF" + android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333 c-13.333 -13.334 -33.333,0 -33.333,0l-160,160c0,0 -40,153.333 -40,173.333c0,13.333,13.333,13.333,13.333,13.333l173.334 -40 c0,0,146.666 -146.666,160 -160C420,200,406.667,180,406.667,180z M226.399,356.823L131.95,378.62l-38.516 -38.522 c7.848 -34.675,20.152 -82.52,23.538 -95.593l3.027,2.162l106.667,106.666L226.399,356.823z" - android:stroke="#FF000000" - android:strokeWidth="10" /> + android:stroke="#FF000000" + android:strokeWidth="10" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable21.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable21.xml new file mode 100644 index 0000000..e0013e7 --- /dev/null +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable21.xml @@ -0,0 +1,51 @@ +<!-- + Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + + <size + android:height="64dp" + android:width="64dp" /> + + <viewport + android:viewportHeight="200" + android:viewportWidth="200" /> + + <group> + <path + android:name="background1" + android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z" + android:fill="#FF000000"/> + <path + android:name="background2" + android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z" + android:fill="#FF000000"/> + </group> + <group + android:pivotX="0" + android:pivotY="0" + android:rotation="90" + android:scaleX="0.75" + android:scaleY="0.5" + android:translateX="100.0" + android:translateY="100.0"> + <path + android:name="twoLines" + android:pathData="M 100,10 v 90 M 10,100 h 90" + android:stroke="#FF00FF00" + android:strokeWidth="10" /> + </group> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml new file mode 100644 index 0000000..8d38cb5 --- /dev/null +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml @@ -0,0 +1,72 @@ +<!-- + Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + + <size + android:height="64dp" + android:width="64dp" /> + + <viewport + android:viewportHeight="400" + android:viewportWidth="400" /> + + <group android:name="backgroundGroup" > + <path + android:name="background1" + android:fill="#80000000" + android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" /> + <path + android:name="background2" + android:fill="#80000000" + android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" /> + </group> + <group + android:name="translateToCenterGroup" + android:translateX="50.0" + android:translateY="90.0" > + <path + android:name="twoLines" + android:pathData="M 0,0 v 100 M 0,0 h 100" + android:stroke="#FFFF0000" + android:strokeWidth="20" /> + + <group + android:name="rotationGroup" + android:pivotX="0.0" + android:pivotY="0.0" + android:rotation="-45.0" > + <path + android:name="twoLines1" + android:pathData="M 0,0 v 100 M 0,0 h 100" + android:stroke="#FF00FF00" + android:strokeWidth="20" /> + + <group + android:name="translateGroup" + android:translateX="130.0" + android:translateY="160.0" > + <group android:name="scaleGroup" > + <path + android:name="twoLines2" + android:pathData="M 0,0 v 100 M 0,0 h 100" + android:stroke="#FF0000FF" + android:strokeWidth="20" /> + </group> + </group> + </group> + </group> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml new file mode 100644 index 0000000..52acd7a --- /dev/null +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml @@ -0,0 +1,86 @@ +<!-- + Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + + <size + android:height="64dp" + android:width="64dp" /> + + <viewport + android:viewportHeight="400" + android:viewportWidth="400" /> + + <group android:name="backgroundGroup" > + <path + android:name="background1" + android:fill="#80000000" + android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" /> + <path + android:name="background2" + android:fill="#80000000" + android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" /> + </group> + <group + android:name="translateToCenterGroup" + android:translateX="50.0" + android:translateY="90.0" > + <path + android:name="twoLines" + android:pathData="@string/twoLinePathData" + android:stroke="#FFFF0000" + android:strokeWidth="20" /> + + <group + android:name="rotationGroup" + android:pivotX="0.0" + android:pivotY="0.0" + android:rotation="-45.0" > + <path + android:name="twoLines1" + android:pathData="@string/twoLinePathData" + android:stroke="#FF00FF00" + android:strokeWidth="20" /> + + <group + android:name="translateGroup" + android:translateX="130.0" + android:translateY="160.0" > + <group android:name="scaleGroup" > + <path + android:name="twoLines3" + android:pathData="@string/twoLinePathData" + android:stroke="#FF0000FF" + android:strokeWidth="20" /> + </group> + </group> + + <group + android:name="translateGroupHalf" + android:translateX="65.0" + android:translateY="80.0" > + <group android:name="scaleGroup" > + <path + android:name="twoLines2" + android:pathData="@string/twoLinePathData" + android:fill="?android:attr/colorForeground" + android:stroke="?android:attr/colorForeground" + android:strokeWidth="20" /> + </group> + </group> + </group> + </group> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml index cda213d..22ce795 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml @@ -23,8 +23,10 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <path - android:fill="#FF000000" - android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" /> + <group> + <path + android:fill="#FF000000" + android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml index 2cb6381..042173c 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml @@ -23,8 +23,10 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <path - android:fill="#FF000000" - android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" /> + <group> + <path + android:fill="#FF000000" + android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml index d58942e..6b6f43d 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml @@ -23,8 +23,10 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <path - android:fill="#FF000000" - android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" /> + <group> + <path + android:fill="#FF000000" + android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml index 4717be4..ba8ebca 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml @@ -23,11 +23,13 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <path - android:fillOpacity="0.9" - android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" /> - <path - android:fillOpacity="0.9" - android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" /> + <group> + <path + android:fillOpacity="0.9" + android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" /> + <path + android:fillOpacity="0.9" + android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml index c626325..896a938 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml @@ -23,8 +23,10 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <path - android:fill="#FF000000" - android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" /> + <group> + <path + android:fill="#FF000000" + android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_test01.xml b/tests/VectorDrawableTest/res/drawable/vector_test01.xml index bad5a46..a9091ab 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_test01.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_test01.xml @@ -23,10 +23,12 @@ limitations under the License. android:viewportHeight="512" android:viewportWidth="512" /> - <path - android:name="002b" - android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299" - android:stroke="#FF0000FF" - android:strokeWidth="4" /> + <group> + <path + android:name="002b" + android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299" + android:stroke="#FF0000FF" + android:strokeWidth="4" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_test02.xml b/tests/VectorDrawableTest/res/drawable/vector_test02.xml index c92b6f4..ab58c06 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_test02.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_test02.xml @@ -23,10 +23,12 @@ limitations under the License. android:viewportHeight="512" android:viewportWidth="512" /> - <path - android:name="002b" - android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299" - android:stroke="#FF0000FF" - android:strokeWidth="4" /> + <group> + <path + android:name="002b" + android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299" + android:stroke="#FF0000FF" + android:strokeWidth="4" /> + </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/values/strings.xml b/tests/VectorDrawableTest/res/values/strings.xml index 64163c2..b49a1aa 100644 --- a/tests/VectorDrawableTest/res/values/strings.xml +++ b/tests/VectorDrawableTest/res/values/strings.xml @@ -15,4 +15,5 @@ --> <resources> + <string name="twoLinePathData" >"M 0,0 v 100 M 0,0 h 100"</string> </resources> diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java index b918cdd..c2a5e6b 100644 --- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java @@ -17,11 +17,11 @@ import android.app.Activity; import android.content.res.Resources; import android.graphics.drawable.VectorDrawable; import android.os.Bundle; -import android.view.View; import android.widget.TextView; import android.widget.Button; import android.widget.GridLayout; import android.widget.ScrollView; + import java.text.DecimalFormat; @SuppressWarnings({"UnusedDeclaration"}) @@ -47,7 +47,10 @@ public class VectorDrawablePerformance extends Activity { R.drawable.vector_drawable17, R.drawable.vector_drawable18, R.drawable.vector_drawable19, - R.drawable.vector_drawable20 + R.drawable.vector_drawable20, + R.drawable.vector_drawable21, + R.drawable.vector_drawable22, + R.drawable.vector_drawable23 }; @Override @@ -68,7 +71,6 @@ public class VectorDrawablePerformance extends Activity { TextView t = new TextView(this); DecimalFormat df = new DecimalFormat("#.##"); t.setText("avgL=" + df.format(time / (icon.length * 1000000.)) + " ms"); - t.setBackgroundColor(0xFF000000); container.addView(t); time = android.os.SystemClock.elapsedRealtimeNanos(); for (int i = 0; i < icon.length; i++) { @@ -81,7 +83,6 @@ public class VectorDrawablePerformance extends Activity { time = android.os.SystemClock.elapsedRealtimeNanos()-time; t = new TextView(this); t.setText("avgS=" + df.format(time / (icon.length * 1000000.)) + " ms"); - t.setBackgroundColor(0xFF000000); container.addView(t); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index e9daffd..e35bc06 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -71,7 +71,7 @@ public final class Canvas_Delegate { * Returns the native delegate associated to a given {@link Canvas} object. */ public static Canvas_Delegate getDelegate(Canvas canvas) { - return sManager.getDelegate(canvas.getNativeCanvas()); + return sManager.getDelegate(canvas.getNativeCanvasWrapper()); } /** @@ -102,7 +102,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static boolean isOpaque(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return false; } @@ -113,7 +113,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getWidth(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return 0; } @@ -124,7 +124,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getHeight(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return 0; } @@ -135,7 +135,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return; } @@ -146,7 +146,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void rotate(Canvas thisCanvas, float degrees) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return; } @@ -157,7 +157,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return; } @@ -168,7 +168,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return; } @@ -204,7 +204,7 @@ public final class Canvas_Delegate { /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right, float bottom) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return false; } @@ -227,7 +227,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int save(Canvas thisCanvas, int saveFlags) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return 0; } @@ -238,7 +238,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void restore(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return; } @@ -249,7 +249,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getSaveCount(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return 0; } @@ -260,7 +260,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvasWrapper()); if (canvasDelegate == null) { return; } @@ -287,7 +287,7 @@ public final class Canvas_Delegate { /*package*/ static void drawLines(Canvas thisCanvas, final float[] pts, final int offset, final int count, Paint paint) { - draw(thisCanvas.getNativeCanvas(), paint.mNativePaint, false /*compositeOnly*/, + draw(thisCanvas.getNativeCanvasWrapper(), paint.mNativePaint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) { diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index f6d7f55..99151c3 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -144,8 +144,7 @@ public class ScanResult implements Parcelable { distanceCm = source.distanceCm; distanceSdCm = source.distanceSdCm; seen = source.seen; - if (source.passpoint != null) - passpoint = new WifiPasspointInfo(source.passpoint); + passpoint = source.passpoint; } } @@ -179,8 +178,7 @@ public class ScanResult implements Parcelable { sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")). append("(cm)"); - if (passpoint != null) - sb.append(", passpoint: [").append(passpoint.toString()).append("]"); + sb.append(", passpoint: ").append(passpoint != null ? "yes" : "no"); return sb.toString(); } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 963117c..5dfc318 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -324,6 +324,24 @@ public class WifiConfiguration implements Parcelable { /** * @hide + * Uid of app creating the configuration + */ + public int creatorUid; + + /** + * @hide + * Uid of last app issuing a connection related command + */ + public int lastConnectUid; + + /** + * @hide + * Uid of last app modifying the configuration + */ + public int lastUpdateUid; + + /** + * @hide * BSSID list on which this configuration was seen. * TODO: prevent this list to grow infinitely, age-out the results */ @@ -441,10 +459,17 @@ public class WifiConfiguration implements Parcelable { /** @hide * if this is set, the WifiConfiguration cannot use linkages so as to bump * it's relative priority. + * - status between and 128 indicate various level of blacklisting depending + * on the severity or frequency of the connection error + * - deleted status indicates that the user is deleting the configuration, and so + * although it may have been self added we will not re-self-add it, ignore it, + * not return it to applications, and not connect to it * */ public static final int AUTO_JOIN_TEMPORARY_DISABLED = 1; /** @hide */ - public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE = 2; + public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE = 128; + /** @hide */ + public static final int AUTO_JOIN_DELETED = 200; /** * @hide @@ -453,11 +478,29 @@ public class WifiConfiguration implements Parcelable { /** * Set if the configuration was self added by the framework + * This boolean is cleared if we get a connect/save/ update or + * any wifiManager command that indicate the user interacted with the configuration + * since we will now consider that the configuration belong to him. * @hide */ public boolean selfAdded; /** + * Set if the configuration was self added by the framework + * This boolean is set once and never cleared. It is used + * so as we never loose track of who created the + * configuration in the first place. + * @hide + */ + public boolean didSelfAdd; + + /** + * peer WifiConfiguration this WifiConfiguration was added for + * @hide + */ + public String peerWifiConfiguration; + + /** * @hide * Indicate that a WifiConfiguration is temporary and should not be saved * nor considered by AutoJoin. @@ -513,6 +556,7 @@ public class WifiConfiguration implements Parcelable { enterpriseConfig = new WifiEnterpriseConfig(); autoJoinStatus = AUTO_JOIN_ENABLED; selfAdded = false; + didSelfAdd = false; ephemeral = false; mIpConfiguration = new IpConfiguration(); } @@ -650,6 +694,10 @@ public class WifiConfiguration implements Parcelable { sbuf.append(mIpConfiguration.toString()); + if (selfAdded) sbuf.append("selfAdded"); + if (creatorUid != 0) sbuf.append("uid=" + Integer.toString(creatorUid)); + + return sbuf.toString(); } @@ -883,6 +931,7 @@ public class WifiConfiguration implements Parcelable { networkId = source.networkId; status = source.status; disableReason = source.disableReason; + disableReason = source.disableReason; SSID = source.SSID; BSSID = source.BSSID; FQDN = source.FQDN; @@ -933,6 +982,11 @@ public class WifiConfiguration implements Parcelable { } lastFailure = source.lastFailure; + didSelfAdd = source.didSelfAdd; + lastConnectUid = source.lastConnectUid; + lastUpdateUid = source.lastUpdateUid; + creatorUid = source.creatorUid; + peerWifiConfiguration = source.peerWifiConfiguration; } } @@ -972,6 +1026,10 @@ public class WifiConfiguration implements Parcelable { dest.writeString(defaultGwMacAddress); dest.writeInt(autoJoinStatus); dest.writeInt(selfAdded ? 1 : 0); + dest.writeInt(didSelfAdd ? 1 : 0); + dest.writeInt(creatorUid); + dest.writeInt(lastConnectUid); + dest.writeInt(lastUpdateUid); /* TODO: should we write the cache results to the parcel? if (scanResultCache != null) { @@ -1017,6 +1075,10 @@ public class WifiConfiguration implements Parcelable { config.defaultGwMacAddress = in.readString(); config.autoJoinStatus = in.readInt(); config.selfAdded = in.readInt() != 0; + config.didSelfAdd = in.readInt() != 0; + config.creatorUid = in.readInt(); + config.lastConnectUid = in.readInt(); + config.lastUpdateUid = in.readInt(); /* TODO: should we write the cache results to the parcel? boolean done = false; diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 1484d49..7debb93 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -224,9 +224,9 @@ public class WifiEnterpriseConfig implements Parcelable { public static final int TTLS = 2; /** EAP-Password */ public static final int PWD = 3; - /** EAP-Subscriber Identity Module */ + /** EAP-Subscriber Identity Module {@hide} */ public static final int SIM = 4; - /** EAP-Authentication and Key Agreement */ + /** EAP-Authentication and Key Agreement {@hide} */ public static final int AKA = 5; /** @hide */ public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA" }; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index bf46745..141a69e 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1413,14 +1413,12 @@ public class WifiManager { /** * Passed with {@link ActionListener#onFailure}. * Indicates that the operation failed due to an internal error. - * @hide */ public static final int ERROR = 0; /** * Passed with {@link ActionListener#onFailure}. * Indicates that the operation is already in progress - * @hide */ public static final int IN_PROGRESS = 1; @@ -1428,30 +1426,28 @@ public class WifiManager { * Passed with {@link ActionListener#onFailure}. * Indicates that the operation failed because the framework is busy and * unable to service the request - * @hide */ public static final int BUSY = 2; /* WPS specific errors */ - /** WPS overlap detected {@hide} */ + /** WPS overlap detected */ public static final int WPS_OVERLAP_ERROR = 3; - /** WEP on WPS is prohibited {@hide} */ + /** WEP on WPS is prohibited */ public static final int WPS_WEP_PROHIBITED = 4; - /** TKIP only prohibited {@hide} */ + /** TKIP only prohibited */ public static final int WPS_TKIP_ONLY_PROHIBITED = 5; - /** Authentication failure on WPS {@hide} */ + /** Authentication failure on WPS */ public static final int WPS_AUTH_FAILURE = 6; - /** WPS timed out {@hide} */ + /** WPS timed out */ public static final int WPS_TIMED_OUT = 7; /** * Passed with {@link ActionListener#onFailure}. * Indicates that the operation failed due to invalid inputs - * @hide */ public static final int INVALID_ARGS = 8; - /** Interface for callback invocation on an application action {@hide} */ + /** Interface for callback invocation on an application action */ public interface ActionListener { /** The operation succeeded */ public void onSuccess(); @@ -1463,7 +1459,7 @@ public class WifiManager { public void onFailure(int reason); } - /** Interface for callback invocation on a start WPS action {@hide} */ + /** Interface for callback invocation on a start WPS action */ public interface WpsListener { /** WPS start succeeded */ public void onStartSuccess(String pin); @@ -1745,7 +1741,6 @@ public class WifiManager { * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again - * @hide */ public void startWps(WpsInfo config, WpsListener listener) { if (config == null) throw new IllegalArgumentException("config cannot be null"); @@ -1759,7 +1754,6 @@ public class WifiManager { * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again - * @hide */ public void cancelWps(ActionListener listener) { validateChannel(); diff --git a/wifi/java/android/net/wifi/WpsInfo.java b/wifi/java/android/net/wifi/WpsInfo.java index 2ad4ad0..ae2e771 100644 --- a/wifi/java/android/net/wifi/WpsInfo.java +++ b/wifi/java/android/net/wifi/WpsInfo.java @@ -40,7 +40,7 @@ public class WpsInfo implements Parcelable { /** Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details */ public int setup; - /** @hide */ + /** Passed with pin method KEYPAD */ public String BSSID; /** Passed with pin method configuration */ diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java index 0769a64..54ac71e 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java @@ -20,10 +20,13 @@ import android.net.wifi.WifiEnterpriseConfig; import android.os.Parcelable; import android.os.Parcel; +import java.util.ArrayList; import java.util.Collection; -import java.util.Set; +import java.util.List; import java.util.Iterator; import java.util.Map; +import java.util.Set; + /** * A class representing a Wi-Fi Passpoint credential. @@ -32,77 +35,90 @@ import java.util.Map; public class WifiPasspointCredential implements Parcelable { private final static String TAG = "PasspointCredential"; - private String mWifiSPFQDN; + private final static boolean DBG = true; + + /** Wi-Fi nodes**/ + private String mWifiSpFqdn; + + /** PerProviderSubscription nodes **/ private String mCredentialName; - private String mUpdateIdentifier; + + /** SubscriptionUpdate nodes **/ + private String mSubscriptionUpdateInterval; private String mSubscriptionUpdateMethod; + private String mSubscriptionUpdateRestriction; + private String mSubscriptionUpdateURI; + private String mSubscriptionUpdateUsername; + private String mSubscriptionUpdatePassword; + + /** HomeSP nodes **/ + private String mHomeSpFqdn; + private String mFriendlyName; + private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList; + private Collection<WifiPasspointDmTree.OtherHomePartners> mOtherHomePartnerList; + + /** SubscriptionParameters nodes**/ + private String mCreationDate; + private String mExpirationDate; + + /** Credential nodes **/ private String mType; private String mInnerMethod; private String mCertType; private String mCertSha256Fingerprint; + private String mUpdateIdentifier; private String mUsername; private String mPasswd; + private String mRealm; private String mImsi; private String mMcc; private String mMnc; private String mCaRootCert; - private String mRealm; - private int mPriority; //User preferred priority; The smaller, the higher - private boolean mUserPreferred = false; - private String mHomeSpFqdn; - private String mFriendlyName; - private String mOtherhomepartnerFqdn; private String mClientCert; - private String mCreationDate; - private String mExpirationDate; - - private String mSubscriptionDMAccUsername; - private String mSubscriptionDMAccPassword; - private String mSubscriptionUpdateInterval; + private boolean mCheckAaaServerCertStatus; - private String mPolicyUpdateURI; + /** Policy nodes **/ + private String mPolicyUpdateUri; private String mPolicyUpdateInterval; - private String mPolicyDMAccUsername; - private String mPolicyDMAccPassword; + private String mPolicyUpdateUsername; + private String mPolicyUpdatePassword; private String mPolicyUpdateRestriction; private String mPolicyUpdateMethod; - private Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> mPreferredRoamingPartnerList; - private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList; private Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> mMinBackhaulThresholdNetwork; - private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple; private Collection<WifiPasspointDmTree.SPExclusionList> mSpExclusionList; + private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple; private String mMaxBssLoad; - private boolean mIsMachineRemediation; + /** CrednetialPriority node **/ + private int mCrednetialPriority; - private String mAAACertURL; - private String mAAASha256Fingerprint; + /** AAAServerTrustRoot nodes **/ + private String mAaaCertUrl; + private String mAaaSha256Fingerprint; - private String mSubscriptionUpdateRestriction; - private String mSubscriptionUpdateURI; - - private boolean mCheckAaaServerCertStatus; + /** Others **/ + private boolean mIsMachineRemediation; + private boolean mUserPreferred = false; + private String mWifiTreePath; + private WifiEnterpriseConfig mEnterpriseConfig; /** @hide */ - public WifiPasspointCredential() { - - } + public WifiPasspointCredential() {} /** * Constructor * @param realm Realm of the passpoint credential - * @param config Credential information, must be either EAP-TLS or EAP-TTLS. + * @param fqdn Fully qualified domain name (FQDN) of the credential + * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS * @see WifiEnterpriseConfig */ - public WifiPasspointCredential(String realm, WifiEnterpriseConfig config) { + public WifiPasspointCredential(String realm, String fqdn, WifiEnterpriseConfig config) { mRealm = realm; switch (config.getEapMethod()) { case WifiEnterpriseConfig.Eap.TLS: - // TODO; - break; case WifiEnterpriseConfig.Eap.TTLS: - // TODO; + mEnterpriseConfig = new WifiEnterpriseConfig(config); break; default: // ignore @@ -113,84 +129,6 @@ public class WifiPasspointCredential implements Parcelable { public WifiPasspointCredential(String type, String caroot, String clientcert, - WifiPasspointDmTree.SpFqdn sp, - WifiPasspointDmTree.CredentialInfo credinfo) { - - if (credinfo == null) { - return; - } - - mType = type; - mCaRootCert = caroot; - mClientCert = clientcert; - - mWifiSPFQDN = sp.nodeName; - mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier; - - mCredentialName = credinfo.nodeName; - Set set = credinfo.homeSP.otherHomePartners.entrySet(); - Iterator i = set.iterator(); - if (i.hasNext()) { - Map.Entry entry3 = (Map.Entry) i.next(); - WifiPasspointDmTree.OtherHomePartners ohp = (WifiPasspointDmTree.OtherHomePartners) entry3.getValue(); - mOtherhomepartnerFqdn = ohp.FQDN; - } - - set = credinfo.aAAServerTrustRoot.entrySet(); - i = set.iterator(); - if (i.hasNext()) { - Map.Entry entry3 = (Map.Entry) i.next(); - WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue(); - mAAACertURL = aaa.CertURL; - mAAASha256Fingerprint = aaa.CertSHA256Fingerprint; - } - - mCertType = credinfo.credential.digitalCertificate.CertificateType; - mCertSha256Fingerprint = credinfo.credential.digitalCertificate.CertSHA256Fingerprint; - mUsername = credinfo.credential.usernamePassword.Username; - mPasswd = credinfo.credential.usernamePassword.Password; - mIsMachineRemediation = credinfo.credential.usernamePassword.MachineManaged; - mInnerMethod = credinfo.credential.usernamePassword.eAPMethod.InnerMethod; - mImsi = credinfo.credential.sim.IMSI; - mCreationDate = credinfo.credential.CreationDate; - mExpirationDate = credinfo.credential.ExpirationDate; - mRealm = credinfo.credential.Realm; - - if (credinfo.credentialPriority == null) { - credinfo.credentialPriority = "128"; - } - mPriority = Integer.parseInt(credinfo.credentialPriority); - - mHomeSpFqdn = credinfo.homeSP.FQDN; - - mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval; - mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod; - mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction; - mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI; - mSubscriptionDMAccUsername = credinfo.subscriptionUpdate.usernamePassword.Username; - mSubscriptionDMAccPassword = credinfo.subscriptionUpdate.usernamePassword.Password; - - mPolicyUpdateURI = credinfo.policy.policyUpdate.URI; - mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval; - mPolicyDMAccUsername = credinfo.policy.policyUpdate.usernamePassword.Username; - mPolicyDMAccPassword = credinfo.policy.policyUpdate.usernamePassword.Password; - mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction; - mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod; - mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values(); - mMinBackhaulThresholdNetwork = credinfo.policy.minBackhaulThreshold.values(); - mRequiredProtoPortTuple = credinfo.policy.requiredProtoPortTuple.values(); - mMaxBssLoad = credinfo.policy.maximumBSSLoadValue; - mSpExclusionList = credinfo.policy.sPExclusionList.values(); - - mHomeOIList = credinfo.homeSP.homeOIList.values(); - mFriendlyName = credinfo.homeSP.FriendlyName; - mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus; - } - - /** @hide */ - public WifiPasspointCredential(String type, - String caroot, - String clientcert, String mcc, String mnc, WifiPasspointDmTree.SpFqdn sp, @@ -204,25 +142,19 @@ public class WifiPasspointCredential implements Parcelable { mCaRootCert = caroot; mClientCert = clientcert; - mWifiSPFQDN = sp.nodeName; + mWifiSpFqdn = sp.nodeName; mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier; mCredentialName = credinfo.nodeName; - Set set = credinfo.homeSP.otherHomePartners.entrySet(); - Iterator i = set.iterator(); - if (i.hasNext()) { - Map.Entry entry3 = (Map.Entry) i.next(); - WifiPasspointDmTree.OtherHomePartners ohp = (WifiPasspointDmTree.OtherHomePartners) entry3.getValue(); - mOtherhomepartnerFqdn = ohp.FQDN; - } + mOtherHomePartnerList = credinfo.homeSP.otherHomePartners.values(); - set = credinfo.aAAServerTrustRoot.entrySet(); - i = set.iterator(); + Set set = credinfo.aAAServerTrustRoot.entrySet(); + Iterator i = set.iterator(); if (i.hasNext()) { Map.Entry entry3 = (Map.Entry) i.next(); WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue(); - mAAACertURL = aaa.CertURL; - mAAASha256Fingerprint = aaa.CertSHA256Fingerprint; + mAaaCertUrl = aaa.CertURL; + mAaaSha256Fingerprint = aaa.CertSHA256Fingerprint; } mCertType = credinfo.credential.digitalCertificate.CertificateType; @@ -239,22 +171,24 @@ public class WifiPasspointCredential implements Parcelable { mRealm = credinfo.credential.Realm; if (credinfo.credentialPriority == null) { - credinfo.credentialPriority = "128"; + mCrednetialPriority = 128; + } else { + mCrednetialPriority = Integer.parseInt(credinfo.credentialPriority); } - mPriority = Integer.parseInt(credinfo.credentialPriority); mHomeSpFqdn = credinfo.homeSP.FQDN; + mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval; mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod; mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction; mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI; - mSubscriptionDMAccUsername = credinfo.subscriptionUpdate.usernamePassword.Username; - mSubscriptionDMAccPassword = credinfo.subscriptionUpdate.usernamePassword.Password; + mSubscriptionUpdateUsername = credinfo.subscriptionUpdate.usernamePassword.Username; + mSubscriptionUpdatePassword = credinfo.subscriptionUpdate.usernamePassword.Password; - mPolicyUpdateURI = credinfo.policy.policyUpdate.URI; + mPolicyUpdateUri = credinfo.policy.policyUpdate.URI; mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval; - mPolicyDMAccUsername = credinfo.policy.policyUpdate.usernamePassword.Username; - mPolicyDMAccPassword = credinfo.policy.policyUpdate.usernamePassword.Password; + mPolicyUpdateUsername = credinfo.policy.policyUpdate.usernamePassword.Username; + mPolicyUpdatePassword = credinfo.policy.policyUpdate.usernamePassword.Password; mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction; mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod; mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values(); @@ -265,6 +199,7 @@ public class WifiPasspointCredential implements Parcelable { mHomeOIList = credinfo.homeSP.homeOIList.values(); mFriendlyName = credinfo.homeSP.FriendlyName; + mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus; } /** @hide */ @@ -283,8 +218,8 @@ public class WifiPasspointCredential implements Parcelable { } /** @hide */ - public String getWifiSPFQDN() { - return mWifiSPFQDN; + public String getWifiSpFqdn() { + return mWifiSpFqdn; } /** @hide */ @@ -293,16 +228,26 @@ public class WifiPasspointCredential implements Parcelable { } /** @hide */ - public String getEapMethodStr() { + public String getType() { return mType; } /** - * Get EAP method of this Passpoint credential. - * @return EAP method, refer to {@link WifiEnterpriseConfig.Eap} for possible return values + * Get enterprise config of this Passpoint credential. + * @return Enterprise config + * @see WifiEnterpriseConfig */ - public int getEapMethod() { - return 0; + public WifiEnterpriseConfig getEnterpriseConfig() { + return new WifiEnterpriseConfig(mEnterpriseConfig); + } + + /** + * Set enterprise config of this Passpoint credential. + * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS + * @see WifiEnterpriseConfig + */ + public void setEnterpriseConfig(WifiEnterpriseConfig config) { + // TODO } /** @hide */ @@ -315,10 +260,7 @@ public class WifiPasspointCredential implements Parcelable { return mCertSha256Fingerprint; } - /** - * Get the user name of this Passpoint credential, for EAP-TTLS only. - * @return user name - */ + /** @hide */ public String getUserName() { return mUsername; } @@ -329,10 +271,7 @@ public class WifiPasspointCredential implements Parcelable { return mPasswd; } - /** - * Get the IMSI of this Passpoint credential, for EAP-SIM / EAP-AKA only. - * @return IMSI - */ + /** @hide */ public String getImsi() { return mImsi; } @@ -348,62 +287,75 @@ public class WifiPasspointCredential implements Parcelable { } /** @hide */ - public String getCaRootCert() { + public String getCaRootCertPath() { return mCaRootCert; } - /** - * Get the client certificate path of this Passpoint credential, for EAP-TLS only. - * @return client certificate path - */ + /** @hide */ public String getClientCertPath() { return mClientCert; } /** - * Get the realm of this Passpoint credential, for all EAP methods. + * Get the realm of this Passpoint credential. * @return Realm */ public String getRealm() { return mRealm; } + /** + * Set the ream of this Passpoint credential. + * @param realm Realm + */ + public void setRealm(String realm) { + mRealm = realm; + } + /** @hide */ public int getPriority() { if (mUserPreferred) { return 0; } - return mPriority; + return mCrednetialPriority; } /** - * Get the fully qualified domain name (FQDN) of this Passpoint credential, - * for all EAP methods. + * Get the fully qualified domain name (FQDN) of this Passpoint credential. * @return FQDN */ - public String getFqdn() { + public String getHomeSpFqdn() { return mHomeSpFqdn; } + /** + * Set the fully qualified domain name (FQDN) of this Passpoint credential. + * @param fqdn FQDN + */ + public void setFqdn(String fqdn) { + mHomeSpFqdn = fqdn; + } + + /** @hide */ - public String getOtherhomepartners() { - return mOtherhomepartnerFqdn; + public Collection<WifiPasspointDmTree.OtherHomePartners> getOtherHomePartnerList() { + return mOtherHomePartnerList; } /** @hide */ - public String getSubscriptionDMAccUsername() { - return mSubscriptionDMAccUsername; + public String getSubscriptionUpdateUsername() { + return mSubscriptionUpdateUsername; } /** @hide */ - public String getSubscriptionDMAccPassword() { - return mSubscriptionDMAccPassword; + public String getSubscriptionUpdatePassword() { + return mSubscriptionUpdatePassword; } /** @hide */ - public String getPolicyUpdateURI() { - return mPolicyUpdateURI; + public String getPolicyUpdateUri() { + return mPolicyUpdateUri; } /** @hide */ @@ -412,13 +364,13 @@ public class WifiPasspointCredential implements Parcelable { } /** @hide */ - public String getPolicyDMAccUsername() { - return mPolicyDMAccUsername; + public String getPolicyUpdateUsername() { + return mPolicyUpdateUsername; } /** @hide */ - public String getPolicyDMAccPassword() { - return mPolicyDMAccPassword; + public String getPolicyUpdatePassword() { + return mPolicyUpdatePassword; } /** @hide */ @@ -447,12 +399,12 @@ public class WifiPasspointCredential implements Parcelable { } /** @hide */ - public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPrpList() { + public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPreferredRoamingPartnerList() { return mPreferredRoamingPartnerList; } /** @hide */ - public Collection<WifiPasspointDmTree.HomeOIList> getHomeOIList() { + public Collection<WifiPasspointDmTree.HomeOIList> getHomeOiList() { return mHomeOIList; } @@ -477,13 +429,13 @@ public class WifiPasspointCredential implements Parcelable { } /** @hide */ - public String getAAACertURL() { - return mAAACertURL; + public String getAaaCertUrl() { + return mAaaCertUrl; } /** @hide */ - public String getAAASha256Fingerprint() { - return mAAASha256Fingerprint; + public String getAaaSha256Fingerprint() { + return mAaaSha256Fingerprint; } /** @hide */ @@ -560,72 +512,75 @@ public class WifiPasspointCredential implements Parcelable { StringBuffer sb = new StringBuffer(); String none = "<none>"; - sb.append(", UpdateIdentifier: ") - .append(mUpdateIdentifier == null ? none : mUpdateIdentifier). - append(", SubscriptionUpdateMethod: ") - .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod). - append(", Type: ").append(mType == null ? none : mType). - append(", Username: ").append(mUsername == null ? none : mUsername). - append(", Passwd: ").append(mPasswd == null ? none : mPasswd). - append(", SubDMAccUsername: ") - .append(mSubscriptionDMAccUsername == null ? none : mSubscriptionDMAccUsername). - append(", SubDMAccPassword: ") - .append(mSubscriptionDMAccPassword == null ? none : mSubscriptionDMAccPassword). - append(", PolDMAccUsername: ") - .append(mPolicyDMAccUsername == null ? none : mPolicyDMAccUsername). - append(", PolDMAccPassword: ") - .append(mPolicyDMAccPassword == null ? none : mPolicyDMAccPassword). - append(", Imsi: ").append(mImsi == null ? none : mImsi). - append(", Mcc: ").append(mMcc == null ? none : mMcc). - append(", Mnc: ").append(mMnc == null ? none : mMnc). - append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert). - append(", Realm: ").append(mRealm == null ? none : mRealm). - append(", Priority: ").append(mPriority). - append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn). - append(", Otherhomepartners: ") - .append(mOtherhomepartnerFqdn == null ? none : mOtherhomepartnerFqdn). - append(", ExpirationDate: ") - .append(mExpirationDate == null ? none : mExpirationDate). - append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad). - append(", SPExclusionList: ").append(mSpExclusionList); - - if (mPreferredRoamingPartnerList != null) { - sb.append("PreferredRoamingPartnerList:"); - for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) { - sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match). - append(", priority:").append(prpListItem.Priority). - append(", country:").append(prpListItem.Country).append("]"); + if (!DBG) { + sb.append(none); + } else { + sb.append(", UpdateIdentifier: ") + .append(mUpdateIdentifier == null ? none : mUpdateIdentifier) + .append(", SubscriptionUpdateMethod: ") + .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod) + .append(", Type: ").append(mType == null ? none : mType) + .append(", Username: ").append(mUsername == null ? none : mUsername) + .append(", Passwd: ").append(mPasswd == null ? none : mPasswd) + .append(", SubDMAccUsername: ") + .append(mSubscriptionUpdateUsername == null ? none : mSubscriptionUpdateUsername) + .append(", SubDMAccPassword: ") + .append(mSubscriptionUpdatePassword == null ? none : mSubscriptionUpdatePassword) + .append(", PolDMAccUsername: ") + .append(mPolicyUpdateUsername == null ? none : mPolicyUpdateUsername) + .append(", PolDMAccPassword: ") + .append(mPolicyUpdatePassword == null ? none : mPolicyUpdatePassword) + .append(", Imsi: ").append(mImsi == null ? none : mImsi) + .append(", Mcc: ").append(mMcc == null ? none : mMcc) + .append(", Mnc: ").append(mMnc == null ? none : mMnc) + .append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert) + .append(", Realm: ").append(mRealm == null ? none : mRealm) + .append(", Priority: ").append(mCrednetialPriority) + .append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn) + .append(", Otherhomepartners: ") + .append(mOtherHomePartnerList == null ? none : mOtherHomePartnerList) + .append(", ExpirationDate: ") + .append(mExpirationDate == null ? none : mExpirationDate) + .append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad) + .append(", SPExclusionList: ").append(mSpExclusionList); + + if (mPreferredRoamingPartnerList != null) { + sb.append("PreferredRoamingPartnerList:"); + for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) { + sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match). + append(", priority:").append(prpListItem.Priority). + append(", country:").append(prpListItem.Country).append("]"); + } } - } - if (mHomeOIList != null) { - sb.append("HomeOIList:"); - for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) { - sb.append("[HomeOI:").append(HomeOIListItem.HomeOI). - append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired). - append("]"); + if (mHomeOIList != null) { + sb.append("HomeOIList:"); + for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) { + sb.append("[HomeOI:").append(HomeOIListItem.HomeOI). + append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired). + append("]"); + } } - } - if (mMinBackhaulThresholdNetwork != null) { - sb.append("BackHaulThreshold:"); - for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) { - sb.append("[networkType:").append(BhtListItem.NetworkType). - append(", dlBandwidth:").append(BhtListItem.DLBandwidth). - append(", ulBandwidth:").append(BhtListItem.ULBandwidth). - append("]"); + if (mMinBackhaulThresholdNetwork != null) { + sb.append("BackHaulThreshold:"); + for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) { + sb.append("[networkType:").append(BhtListItem.NetworkType). + append(", dlBandwidth:").append(BhtListItem.DLBandwidth). + append(", ulBandwidth:").append(BhtListItem.ULBandwidth). + append("]"); + } } - } - if (mRequiredProtoPortTuple != null) { - sb.append("WifiMORequiredProtoPortTupleList:"); - for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) { - sb.append("[IPProtocol:").append(RpptListItem.IPProtocol). - append(", PortNumber:").append(RpptListItem.PortNumber). - append("]"); + if (mRequiredProtoPortTuple != null) { + sb.append("WifiMORequiredProtoPortTupleList:"); + for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) { + sb.append("[IPProtocol:").append(RpptListItem.IPProtocol). + append(", PortNumber:").append(RpptListItem.PortNumber). + append("]"); + } } } - return sb.toString(); } @@ -636,19 +591,22 @@ public class WifiPasspointCredential implements Parcelable { /** Implement the Parcelable interface {@hide} */ public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mWifiSpFqdn); + dest.writeString(mCredentialName); dest.writeString(mType); - dest.writeString(mUsername); - dest.writeString(mPasswd); - dest.writeString(mImsi); - dest.writeString(mMcc); - dest.writeString(mMnc); - dest.writeString(mCaRootCert); - dest.writeString(mRealm); - dest.writeInt(mPriority); + dest.writeInt(mCrednetialPriority); dest.writeString(mHomeSpFqdn); - dest.writeString(mOtherhomepartnerFqdn); - dest.writeString(mClientCert); - dest.writeString(mExpirationDate); + dest.writeString(mRealm); + } + + /** Implement the Parcelable interface {@hide} */ + public void readFromParcel(Parcel in) { + mWifiSpFqdn = in.readString(); + mCredentialName = in.readString(); + mType = in.readString(); + mCrednetialPriority = in.readInt(); + mHomeSpFqdn = in.readString(); + mRealm = in.readString(); } /** Implement the Parcelable interface {@hide} */ @@ -656,19 +614,12 @@ public class WifiPasspointCredential implements Parcelable { new Creator<WifiPasspointCredential>() { public WifiPasspointCredential createFromParcel(Parcel in) { WifiPasspointCredential pc = new WifiPasspointCredential(); + pc.mWifiSpFqdn = in.readString(); + pc.mCredentialName = in.readString(); pc.mType = in.readString(); - pc.mUsername = in.readString(); - pc.mPasswd = in.readString(); - pc.mImsi = in.readString(); - pc.mMcc = in.readString(); - pc.mMnc = in.readString(); - pc.mCaRootCert = in.readString(); - pc.mRealm = in.readString(); - pc.mPriority = in.readInt(); + pc.mCrednetialPriority = in.readInt(); pc.mHomeSpFqdn = in.readString(); - pc.mOtherhomepartnerFqdn = in.readString(); - pc.mClientCert = in.readString(); - pc.mExpirationDate = in.readString(); + pc.mRealm = in.readString(); return pc; } @@ -681,9 +632,9 @@ public class WifiPasspointCredential implements Parcelable { public int compareTo(WifiPasspointCredential another) { //The smaller the higher - if (mPriority < another.mPriority) { + if (mCrednetialPriority < another.mCrednetialPriority) { return -1; - } else if (mPriority == another.mPriority) { + } else if (mCrednetialPriority == another.mCrednetialPriority) { return this.mType.compareTo(another.mType); } else { return 1; diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java index 9ff1973..bbf5fc6 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java @@ -25,19 +25,17 @@ import java.util.HashMap; /** * Required Mobile Device Management Tree Structure * - * +----------+ - * | ./(Root) | - * +----+-----+ - * | - * +---------+ | +---------+ +---------+ - * | DevInfo |-----------+---------| Wi-Fi |---|SP FQDN* | - * +---------+ | +---------+ +---------+ - * +---------+ | - * |DevDetail|-----------+ - * +---------+ - * - * For example, - * ./Wi-Fi/wi-fi.org/PerproviderSubscription/Cred01/Policy/PreferredRoamingPartnerList/Roa01/FQDN_Math + * +----------+ + * | ./(Root) | + * +----+-----+ + * | + * +---------+ | +---------+ +---------+ + * | DevInfo |-----------+---------| Wi-Fi |--|SP FQDN* | + * +---------+ | +---------+ +---------+ + * +---------+ | | + * |DevDetail|-----------+ +-----------------------+ + * +---------+ |PerproviderSubscription|--<X>+ + * +-----------------------+ * * This class contains all nodes start from Wi-Fi * @hide diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java index 99bea2f..5ef1bf9 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java @@ -88,68 +88,174 @@ public class WifiPasspointInfo implements Parcelable { CONNECTION_CAPABILITY | OSU_PROVIDER; - /** TODO doc */ - public String bssid; - /** TODO doc */ - public String venueName; + public static class WanMetrics { + public static final int STATUS_RESERVED = 0; + public static final int STATUS_UP = 1; + public static final int STATUS_DOWN = 2; + public static final int STATUS_TEST = 3; + + public int wanInfo; + public long downlinkSpeed; + public long uplinkSpeed; + public int downlinkLoad; + public int uplinkLoad; + public int lmd; + + public int getLinkStatus() { + return wanInfo & 0x3; + } - /** TODO doc */ - public String networkAuthType; + public boolean getSymmetricLink() { + return (wanInfo & (1 << 2)) != 0; + } - /** TODO doc */ - public String roamingConsortium; + public boolean getAtCapacity() { + return (wanInfo & (1 << 3)) != 0; + } - /** TODO doc */ - public String ipAddrTypeAvaibility; + @Override + public String toString() { + return wanInfo + "," + downlinkSpeed + "," + uplinkSpeed + "," + + downlinkLoad + "," + uplinkLoad + "," + lmd; + } + } - /** TODO doc */ - public String naiRealm; + public static class IpProtoPort { + public static final int STATUS_CLOSED = 0; + public static final int STATUS_OPEN = 1; + public static final int STATUS_UNKNOWN = 2; - /** TODO doc */ - public String cellularNetwork; + public int proto; + public int port; + public int status; - /** TODO doc */ - public String domainName; + @Override + public String toString() { + return proto + "," + port + "," + status; + } + } - /** TODO doc */ - public String operatorFriendlyName; + public static class NetworkAuthType { + public static final int TYPE_TERMS_AND_CONDITION = 0; + public static final int TYPE_ONLINE_ENROLLMENT = 1; + public static final int TYPE_HTTP_REDIRECTION = 2; + public static final int TYPE_DNS_REDIRECTION = 3; - /** TODO doc */ - public String wanMetrics; + public int type; + public String redirectUrl; - /** TODO doc */ - public String connectionCapability; + @Override + public String toString() { + return type + "," + redirectUrl; + } + } - /** TODO doc */ - public List<WifiPasspointOsuProvider> osuProviderList; + public static class IpAddressType { + public static final int IPV6_NOT_AVAILABLE = 0; + public static final int IPV6_AVAILABLE = 1; + public static final int IPV6_UNKNOWN = 2; + + public static final int IPV4_NOT_AVAILABLE = 0; + public static final int IPV4_PUBLIC = 1; + public static final int IPV4_PORT_RESTRICTED = 2; + public static final int IPV4_SINGLE_NAT = 3; + public static final int IPV4_DOUBLE_NAT = 4; + public static final int IPV4_PORT_RESTRICTED_SINGLE_NAT = 5; + public static final int IPV4_PORT_RESTRICTED_DOUBLE_NAT = 6; + public static final int IPV4_PORT_UNKNOWN = 7; + + private static final int NULL_VALUE = -1; + + public int availability; + + public int getIpv6Availability() { + return availability & 0x3; + } + + public int getIpv4Availability() { + return (availability & 0xFF) >> 2; + } + + @Override + public String toString() { + return getIpv6Availability() + "," + getIpv4Availability(); + } + } + + public static class NaiRealm { + public static final int ENCODING_RFC4282 = 0; + public static final int ENCODING_UTF8 = 1; + + public int encoding; + public String realm; - /** default constructor @hide */ - public WifiPasspointInfo() { - // osuProviderList = new ArrayList<OsuProvider>(); + @Override + public String toString() { + return encoding + "," + realm; + } } - /** copy constructor @hide */ - public WifiPasspointInfo(WifiPasspointInfo source) { - // TODO - bssid = source.bssid; - venueName = source.venueName; - networkAuthType = source.networkAuthType; - roamingConsortium = source.roamingConsortium; - ipAddrTypeAvaibility = source.ipAddrTypeAvaibility; - naiRealm = source.naiRealm; - cellularNetwork = source.cellularNetwork; - domainName = source.domainName; - operatorFriendlyName = source.operatorFriendlyName; - wanMetrics = source.wanMetrics; - connectionCapability = source.connectionCapability; - if (source.osuProviderList != null) { - osuProviderList = new ArrayList<WifiPasspointOsuProvider>(); - for (WifiPasspointOsuProvider osu : source.osuProviderList) - osuProviderList.add(new WifiPasspointOsuProvider(osu)); + public static class CellularNetwork { + public byte[] rawData; + + public int getMnc() { + // TODO + return 0; + } + + public int getMcc() { + // TODO + return 0; + } + + @Override + public String toString() { + if (rawData == null) return null; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < rawData.length; i++) + sb.append(String.format("%02X", rawData[i])); + return sb.toString(); } + } + /** BSSID */ + public String bssid; + + /** venue name */ + public String venueName; + + /** list of network authentication types */ + public List<NetworkAuthType> networkAuthType; + + /** list of roaming consortium OIs */ + public List<String> roamingConsortium; + + /** IP address availability */ + public IpAddressType ipAddrTypeAvailability; + + /** NAI realm */ + public List<NaiRealm> naiRealm; + + /** 3GPP cellular network */ + public CellularNetwork cellularNetwork; + + /** fully qualified domain name (FQDN) */ + public List<String> domainName; + + /** HS 2.0 operator friendly name */ + public String operatorFriendlyName; + + /** HS 2.0 wan metrics */ + public WanMetrics wanMetrics; + + /** HS 2.0 list of IP proto port */ + public List<IpProtoPort> connectionCapability; + + /** HS 2.0 list of OSU providers */ + public List<WifiPasspointOsuProvider> osuProviderList; + /** * Convert mask to ANQP subtypes, for supplicant command use. * @@ -193,46 +299,149 @@ public class WifiPasspointInfo implements Parcelable { @Override public String toString() { StringBuffer sb = new StringBuffer(); + sb.append("BSSID: ").append(bssid); + if (venueName != null) - sb.append(" venueName: ").append(venueName); - if (networkAuthType != null) - sb.append(" networkAuthType: ").append(networkAuthType); - if (roamingConsortium != null) - sb.append(" roamingConsortium: ").append(roamingConsortium); - if (ipAddrTypeAvaibility != null) - sb.append(" ipAddrTypeAvaibility: ").append(ipAddrTypeAvaibility); - if (naiRealm != null) - sb.append(" naiRealm: ").append(naiRealm); + sb.append(" venueName: ").append(venueName.replace("\n", "\\n")); + + if (networkAuthType != null) { + sb.append(" networkAuthType: "); + for (NetworkAuthType auth : networkAuthType) + sb.append("(").append(auth.toString()).append(")"); + } + + if (roamingConsortium != null) { + sb.append(" roamingConsortium: "); + for (String oi : roamingConsortium) + sb.append("(").append(oi).append(")"); + } + + if (ipAddrTypeAvailability != null) { + sb.append(" ipAddrTypeAvaibility: ").append("(") + .append(ipAddrTypeAvailability.toString()).append(")"); + } + + if (naiRealm != null) { + sb.append(" naiRealm: "); + for (NaiRealm realm : naiRealm) + sb.append("(").append(realm.toString()).append(")"); + } + if (cellularNetwork != null) - sb.append(" cellularNetwork: ").append(cellularNetwork); - if (domainName != null) - sb.append(" domainName: ").append(domainName); + sb.append(" cellularNetwork: ").append("(") + .append(cellularNetwork.toString()).append(")"); + + if (domainName != null) { + sb.append(" domainName: "); + for (String fqdn : domainName) + sb.append("(").append(fqdn).append(")"); + } + if (operatorFriendlyName != null) - sb.append(" operatorFriendlyName: ").append(operatorFriendlyName); + sb.append(" operatorFriendlyName: ").append("(") + .append(operatorFriendlyName).append(")"); + if (wanMetrics != null) - sb.append(" wanMetrics: ").append(wanMetrics); - if (connectionCapability != null) - sb.append(" connectionCapability: ").append(connectionCapability); - if (osuProviderList != null) - sb.append(" osuProviderList: (size=" + osuProviderList.size() + ")"); + sb.append(" wanMetrics: ").append("(") + .append(wanMetrics.toString()).append(")"); + + if (connectionCapability != null) { + sb.append(" connectionCapability: "); + for (IpProtoPort ip : connectionCapability) + sb.append("(").append(ip.toString()).append(")"); + } + + if (osuProviderList != null) { + sb.append(" osuProviderList: "); + for (WifiPasspointOsuProvider osu : osuProviderList) + sb.append("(").append(osu.toString()).append(")"); + } + return sb.toString(); } /** Implement the Parcelable interface {@hide} */ @Override public void writeToParcel(Parcel out, int flags) { - out.writeValue(bssid); - out.writeValue(venueName); - out.writeValue(networkAuthType); - out.writeValue(roamingConsortium); - out.writeValue(ipAddrTypeAvaibility); - out.writeValue(naiRealm); - out.writeValue(cellularNetwork); - out.writeValue(domainName); - out.writeValue(operatorFriendlyName); - out.writeValue(wanMetrics); - out.writeValue(connectionCapability); + out.writeString(bssid); + out.writeString(venueName); + + if (networkAuthType == null) { + out.writeInt(0); + } else { + out.writeInt(networkAuthType.size()); + for (NetworkAuthType auth : networkAuthType) { + out.writeInt(auth.type); + out.writeString(auth.redirectUrl); + } + } + + if (roamingConsortium == null) { + out.writeInt(0); + } else { + out.writeInt(roamingConsortium.size()); + for (String oi : roamingConsortium) + out.writeString(oi); + } + + if (ipAddrTypeAvailability == null) { + out.writeInt(IpAddressType.NULL_VALUE); + } else { + out.writeInt(ipAddrTypeAvailability.availability); + } + + if (naiRealm == null) { + out.writeInt(0); + } else { + out.writeInt(naiRealm.size()); + for (NaiRealm realm : naiRealm) { + out.writeInt(realm.encoding); + out.writeString(realm.realm); + } + } + + if (cellularNetwork == null) { + out.writeInt(0); + } else { + out.writeInt(cellularNetwork.rawData.length); + out.writeByteArray(cellularNetwork.rawData); + } + + + if (domainName == null) { + out.writeInt(0); + } else { + out.writeInt(domainName.size()); + for (String fqdn : domainName) + out.writeString(fqdn); + } + + out.writeString(operatorFriendlyName); + + if (wanMetrics == null) { + out.writeInt(0); + } else { + out.writeInt(1); + out.writeInt(wanMetrics.wanInfo); + out.writeLong(wanMetrics.downlinkSpeed); + out.writeLong(wanMetrics.uplinkSpeed); + out.writeInt(wanMetrics.downlinkLoad); + out.writeInt(wanMetrics.uplinkLoad); + out.writeInt(wanMetrics.lmd); + } + + if (connectionCapability == null) { + out.writeInt(0); + } else { + out.writeInt(connectionCapability.size()); + for (IpProtoPort ip : connectionCapability) { + out.writeInt(ip.proto); + out.writeInt(ip.port); + out.writeInt(ip.status); + } + } + if (osuProviderList == null) { out.writeInt(0); } else { @@ -254,18 +463,86 @@ public class WifiPasspointInfo implements Parcelable { @Override public WifiPasspointInfo createFromParcel(Parcel in) { WifiPasspointInfo p = new WifiPasspointInfo(); - p.bssid = (String) in.readValue(String.class.getClassLoader()); - p.venueName = (String) in.readValue(String.class.getClassLoader()); - p.networkAuthType = (String) in.readValue(String.class.getClassLoader()); - p.roamingConsortium = (String) in.readValue(String.class.getClassLoader()); - p.ipAddrTypeAvaibility = (String) in.readValue(String.class.getClassLoader()); - p.naiRealm = (String) in.readValue(String.class.getClassLoader()); - p.cellularNetwork = (String) in.readValue(String.class.getClassLoader()); - p.domainName = (String) in.readValue(String.class.getClassLoader()); - p.operatorFriendlyName = (String) in.readValue(String.class.getClassLoader()); - p.wanMetrics = (String) in.readValue(String.class.getClassLoader()); - p.connectionCapability = (String) in.readValue(String.class.getClassLoader()); - int n = in.readInt(); + int n; + + p.bssid = in.readString(); + p.venueName = in.readString(); + + n = in.readInt(); + if (n > 0) { + p.networkAuthType = new ArrayList<NetworkAuthType>(); + for (int i = 0; i < n; i++) { + NetworkAuthType auth = new NetworkAuthType(); + auth.type = in.readInt(); + auth.redirectUrl = in.readString(); + p.networkAuthType.add(auth); + } + } + + n = in.readInt(); + if (n > 0) { + p.roamingConsortium = new ArrayList<String>(); + for (int i = 0; i < n; i++) + p.roamingConsortium.add(in.readString()); + } + + n = in.readInt(); + if (n != IpAddressType.NULL_VALUE) { + p.ipAddrTypeAvailability = new IpAddressType(); + p.ipAddrTypeAvailability.availability = n; + } + + n = in.readInt(); + if (n > 0) { + p.naiRealm = new ArrayList<NaiRealm>(); + for (int i = 0; i < n; i++) { + NaiRealm realm = new NaiRealm(); + realm.encoding = in.readInt(); + realm.realm = in.readString(); + p.naiRealm.add(realm); + } + } + + n = in.readInt(); + if (n > 0) { + p.cellularNetwork = new CellularNetwork(); + p.cellularNetwork.rawData = new byte[n]; + in.readByteArray(p.cellularNetwork.rawData); + } + + n = in.readInt(); + if (n > 0) { + p.domainName = new ArrayList<String>(); + for (int i = 0; i < n; i++) + p.domainName.add(in.readString()); + } + + p.operatorFriendlyName = in.readString(); + + n = in.readInt(); + if (n > 0) { + p.wanMetrics = new WanMetrics(); + p.wanMetrics.wanInfo = in.readInt(); + p.wanMetrics.downlinkSpeed = in.readLong(); + p.wanMetrics.uplinkSpeed = in.readLong(); + p.wanMetrics.downlinkLoad = in.readInt(); + p.wanMetrics.uplinkLoad = in.readInt(); + p.wanMetrics.lmd = in.readInt(); + } + + n = in.readInt(); + if (n > 0) { + p.connectionCapability = new ArrayList<IpProtoPort>(); + for (int i = 0; i < n; i++) { + IpProtoPort ip = new IpProtoPort(); + ip.proto = in.readInt(); + ip.port = in.readInt(); + ip.status = in.readInt(); + p.connectionCapability.add(ip); + } + } + + n = in.readInt(); if (n > 0) { p.osuProviderList = new ArrayList<WifiPasspointOsuProvider>(); for (int i = 0; i < n; i++) { @@ -274,6 +551,7 @@ public class WifiPasspointInfo implements Parcelable { p.osuProviderList.add(osu); } } + return p; } diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java index e140c13..55acbad 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java @@ -444,10 +444,9 @@ public class WifiPasspointManager { return null; } - /* TODO: add credential APIs */ - /** - * Give a list of all saved Passpoint credentials. + * Get a list of saved Passpoint credentials. Only those credentials owned + * by the caller will be returned. * * @return The list of credentials */ diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java index 18a8f1e..f40dc4f 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java @@ -94,10 +94,10 @@ public class WifiPasspointOsuProvider implements Parcelable { sb.append(" serverUri: ").append(serverUri); sb.append(" osuMethod: ").append(osuMethod); if (iconFileName != null) { - sb.append(" icon: [").append(iconWidth).append("x") + sb.append(" icon: <").append(iconWidth).append("x") .append(iconHeight).append(" ") .append(iconType).append(" ") - .append(iconFileName); + .append(iconFileName).append(">"); } if (osuNai != null) sb.append(" osuNai: ").append(osuNai); @@ -113,16 +113,16 @@ public class WifiPasspointOsuProvider implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeValue(ssid); - out.writeValue(friendlyName); - out.writeValue(serverUri); + out.writeString(ssid); + out.writeString(friendlyName); + out.writeString(serverUri); out.writeInt(osuMethod); out.writeInt(iconWidth); out.writeInt(iconHeight); - out.writeValue(iconType); - out.writeValue(iconFileName); - out.writeValue(osuNai); - out.writeValue(osuService); + out.writeString(iconType); + out.writeString(iconFileName); + out.writeString(osuNai); + out.writeString(osuService); // TODO: icon image? } @@ -131,16 +131,16 @@ public class WifiPasspointOsuProvider implements Parcelable { @Override public WifiPasspointOsuProvider createFromParcel(Parcel in) { WifiPasspointOsuProvider osu = new WifiPasspointOsuProvider(); - osu.ssid = (String) in.readValue(String.class.getClassLoader()); - osu.friendlyName = (String) in.readValue(String.class.getClassLoader()); - osu.serverUri = (String) in.readValue(String.class.getClassLoader()); + osu.ssid = in.readString(); + osu.friendlyName = in.readString(); + osu.serverUri = in.readString(); osu.osuMethod = in.readInt(); osu.iconWidth = in.readInt(); osu.iconHeight = in.readInt(); - osu.iconType = (String) in.readValue(String.class.getClassLoader()); - osu.iconFileName = (String) in.readValue(String.class.getClassLoader()); - osu.osuNai = (String) in.readValue(String.class.getClassLoader()); - osu.osuService = (String) in.readValue(String.class.getClassLoader()); + osu.iconType = in.readString(); + osu.iconFileName = in.readString(); + osu.osuNai = in.readString(); + osu.osuService = in.readString(); return osu; } diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java index 5f76562..9fccf0a 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java @@ -35,7 +35,7 @@ public class WifiPasspointPolicy implements Parcelable { public static final int UNRESTRICTED = 2; private String mName; - private int mSubscriptionPriority; + private int mCredentialPriority; private int mRoamingPriority; private String mBssid; private String mSsid; @@ -44,11 +44,13 @@ public class WifiPasspointPolicy implements Parcelable { private boolean mIsHomeSp; /** @hide */ - public WifiPasspointPolicy(String name, int priority, String ssid, + public WifiPasspointPolicy(String name, String ssid, String bssid, WifiPasspointCredential pc, int restriction, boolean ishomesp) { mName = name; - mSubscriptionPriority = priority; + if (pc != null) { + mCredentialPriority = pc.getPriority(); + } //PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>/Priority mRoamingPriority = 128; //default priority value of 128 mSsid = ssid; @@ -102,8 +104,8 @@ public class WifiPasspointPolicy implements Parcelable { } /** @hide */ - public void setSubscriptionPriority(int priority) { - mSubscriptionPriority = priority; + public void setCredentialPriority(int priority) { + mCredentialPriority = priority; } /** @hide */ @@ -111,8 +113,8 @@ public class WifiPasspointPolicy implements Parcelable { mRoamingPriority = priority; } - public int getSubscriptionPriority() { - return mSubscriptionPriority; + public int getCredentialPriority() { + return mCredentialPriority; } public int getRoamingPriority() { @@ -132,11 +134,11 @@ public class WifiPasspointPolicy implements Parcelable { return -1; } else if ((this.mIsHomeSp == true && another.getHomeSp() == true)) { Log.d(TAG, "both HomeSP"); - //if both home sp, compare subscription priority - if (this.mSubscriptionPriority < another.getSubscriptionPriority()) { + //if both home sp, compare credential priority + if (this.mCredentialPriority < another.getCredentialPriority()) { Log.d(TAG, "this priority is higher"); return -1; - } else if (this.mSubscriptionPriority == another.getSubscriptionPriority()) { + } else if (this.mCredentialPriority == another.getCredentialPriority()) { Log.d(TAG, "both priorities equal"); //if priority still the same, compare name(ssid) if (this.mName.compareTo(another.mName) != 0) { @@ -192,7 +194,7 @@ public class WifiPasspointPolicy implements Parcelable { @Override /** @hide */ public String toString() { - return "PasspointPolicy: name=" + mName + " SubscriptionPriority=" + mSubscriptionPriority + + return "PasspointPolicy: name=" + mName + " CredentialPriority=" + mCredentialPriority + " mRoamingPriority" + mRoamingPriority + " ssid=" + mSsid + " restriction=" + mRestriction + " ishomesp=" + mIsHomeSp + " Credential=" + mCredential; |