diff options
305 files changed, 8608 insertions, 4382 deletions
@@ -78,6 +78,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IServiceConnection.aidl \ core/java/android/app/IStopUserCallback.aidl \ core/java/android/app/task/ITaskCallback.aidl \ + core/java/android/app/task/ITaskManager.aidl \ core/java/android/app/task/ITaskService.aidl \ core/java/android/app/IThumbnailRetriever.aidl \ core/java/android/app/ITransientNotification.aidl \ @@ -328,6 +329,7 @@ LOCAL_SRC_FILES += \ telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl \ telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \ telecomm/java/com/android/internal/telecomm/IInCallService.aidl \ + telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \ telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \ telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ telephony/java/com/android/internal/telephony/ITelephony.aidl \ @@ -379,55 +381,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.tv\* \ - 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 @@ -445,16 +406,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 db18714..05a3072 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 @@ -5152,10 +5154,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); @@ -5230,8 +5232,8 @@ 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 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 @@ -5350,6 +5352,58 @@ package android.app.maintenance { package android.app.task { + public class Task implements android.os.Parcelable { + method public int describeContents(); + method public int getBackoffPolicy(); + method public android.os.Bundle getExtras(); + method public int getId(); + method public long getInitialBackoffMillis(); + method public long getIntervalMillis(); + method public long getMaxExecutionDelayMillis(); + method public long getMinLatencyMillis(); + method public int getNetworkCapabilities(); + method public android.content.ComponentName getService(); + method public boolean isPeriodic(); + method public boolean isRequireCharging(); + method public boolean isRequireDeviceIdle(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public static abstract interface Task.BackoffPolicy { + field public static final int EXPONENTIAL = 1; // 0x1 + field public static final int LINEAR = 0; // 0x0 + } + + public static final class Task.Builder { + ctor public Task.Builder(int, android.content.ComponentName); + method public android.app.task.Task build(); + method public android.app.task.Task.Builder setBackoffCriteria(long, int); + method public android.app.task.Task.Builder setExtras(android.os.Bundle); + method public android.app.task.Task.Builder setMinimumLatency(long); + method public android.app.task.Task.Builder setOverrideDeadline(long); + method public android.app.task.Task.Builder setPeriodic(long); + method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int); + method public android.app.task.Task.Builder setRequiresCharging(boolean); + method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean); + } + + public static abstract interface Task.NetworkType { + field public static final int ANY = 1; // 0x1 + field public static final int NONE = 0; // 0x0 + field public static final int UNMETERED = 2; // 0x2 + } + + public abstract class TaskManager { + ctor public TaskManager(); + method public abstract void cancel(int); + method public abstract void cancelAll(); + method public abstract java.util.List<android.app.task.Task> getAllPendingTasks(); + method public abstract int schedule(android.app.task.Task); + field public static final int RESULT_FAILURE = 0; // 0x0 + field public static final int RESULT_SUCCESS = 1; // 0x1 + } + public class TaskParams implements android.os.Parcelable { method public int describeContents(); method public android.os.Bundle getExtras(); @@ -6973,6 +7027,7 @@ package android.content { 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"; + field public static final java.lang.String TASK_SERVICE = "task"; field public static final java.lang.String TELEPHONY_SERVICE = "phone"; field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices"; field public static final java.lang.String TV_INPUT_SERVICE = "tv_input"; @@ -7911,55 +7966,6 @@ package android.content { method public abstract void onStatusChanged(int); } - public class Task implements android.os.Parcelable { - method public int describeContents(); - method public int getBackoffPolicy(); - method public android.os.Bundle getExtras(); - method public long getInitialBackoffMillis(); - method public long getIntervalMillis(); - method public long getMaxExecutionDelayMillis(); - method public long getMinLatencyMillis(); - method public int getNetworkCapabilities(); - method public android.content.ComponentName getService(); - method public int getTaskId(); - method public boolean isPeriodic(); - method public boolean isRequireCharging(); - method public boolean isRequireDeviceIdle(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator CREATOR; - } - - public static abstract interface Task.BackoffPolicy { - field public static final int EXPONENTIAL = 1; // 0x1 - field public static final int LINEAR = 0; // 0x0 - } - - public final class Task.Builder { - ctor public Task.Builder(int, android.content.ComponentName); - method public android.content.Task build(); - method public android.content.Task.Builder setBackoffCriteria(long, int); - method public android.content.Task.Builder setExtras(android.os.Bundle); - method public android.content.Task.Builder setMinimumLatency(long); - method public android.content.Task.Builder setOverrideDeadline(long); - method public android.content.Task.Builder setPeriodic(long); - method public android.content.Task.Builder setRequiredNetworkCapabilities(int); - method public android.content.Task.Builder setRequiresCharging(boolean); - method public android.content.Task.Builder setRequiresDeviceIdle(boolean); - } - - public static abstract interface Task.NetworkType { - field public static final int ANY = 0; // 0x0 - field public static final int UNMETERED = 1; // 0x1 - } - - public abstract class TaskManager { - ctor public TaskManager(); - method public abstract void cancel(int); - method public abstract void cancelAll(); - method public abstract java.util.List<android.content.Task> getAllPendingTasks(); - method public abstract int schedule(android.content.Task); - } - public class UriMatcher { ctor public UriMatcher(int); method public void addURI(java.lang.String, java.lang.String, int); @@ -11548,6 +11554,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 { @@ -11918,16 +11934,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"; @@ -11949,6 +11968,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 @@ -11962,6 +11999,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 @@ -11971,6 +12009,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 { @@ -12072,6 +12129,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 @@ -13757,7 +13815,6 @@ package android.media { } 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 @@ -17108,11 +17165,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 } @@ -17532,14 +17587,14 @@ package android.net.wifi.p2p.nsd { package android.net.wifi.passpoint { public class WifiPasspointCredential implements android.os.Parcelable { - ctor public WifiPasspointCredential(java.lang.String, android.net.wifi.WifiEnterpriseConfig); + ctor public WifiPasspointCredential(java.lang.String, java.lang.String, android.net.wifi.WifiEnterpriseConfig); method public int describeContents(); - method public java.lang.String getClientCertPath(); - method public int getEapMethod(); + method public android.net.wifi.WifiEnterpriseConfig getEnterpriseConfig(); method public java.lang.String getFqdn(); - method public java.lang.String getImsi(); method public java.lang.String getRealm(); - method public java.lang.String getUserName(); + method public void setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig); + method public void setFqdn(java.lang.String); + method public void setRealm(java.lang.String); method public void writeToParcel(android.os.Parcel, int); } @@ -21669,7 +21724,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 { @@ -28200,12 +28257,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 @@ -32891,6 +32944,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>); @@ -36584,6 +36638,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/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 5454b46..b7c2c22 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1146,12 +1146,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 +1174,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 +1209,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 +1608,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 +1622,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/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 1634d11..ff8688d 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -133,10 +133,12 @@ import android.view.textservice.TextServicesManager; import android.accounts.AccountManager; import android.accounts.IAccountManager; import android.app.admin.DevicePolicyManager; +import android.app.task.ITaskManager; import android.app.trust.TrustManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsService; +import com.android.internal.appwidget.IAppWidgetService.Stub; import com.android.internal.os.IDropBoxManagerService; import java.io.File; @@ -693,6 +695,12 @@ class ContextImpl extends Context { public Object createService(ContextImpl ctx) { return new UsageStatsManager(ctx.getOuterContext()); }}); + + registerService(TASK_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(TASK_SERVICE); + return new TaskManagerImpl(ITaskManager.Stub.asInterface(b)); + }}); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java new file mode 100644 index 0000000..f42839e --- /dev/null +++ b/core/java/android/app/TaskManagerImpl.java @@ -0,0 +1,62 @@ +/* + * 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. + */ + +// in android.app so ContextImpl has package access +package android.app; + +import android.app.task.ITaskManager; +import android.app.task.Task; +import android.app.task.TaskManager; + +import java.util.List; + + +/** + * Concrete implementation of the TaskManager interface + * @hide + */ +public class TaskManagerImpl extends TaskManager { + ITaskManager mBinder; + + /* package */ TaskManagerImpl(ITaskManager binder) { + mBinder = binder; + } + + @Override + public int schedule(Task task) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void cancel(int taskId) { + // TODO Auto-generated method stub + + } + + @Override + public void cancelAll() { + // TODO Auto-generated method stub + + } + + @Override + public List<Task> getAllPendingTasks() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 77b1acf..6b486c4 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -176,15 +176,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 +1952,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 +1969,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); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 3c08c14..cc6d715 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -122,8 +122,8 @@ interface IDevicePolicyManager { Bundle getApplicationRestrictions(in ComponentName who, in String packageName); 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/task/ITaskManager.aidl b/core/java/android/app/task/ITaskManager.aidl new file mode 100644 index 0000000..b56c78a --- /dev/null +++ b/core/java/android/app/task/ITaskManager.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.app.task; + +import android.app.task.Task; + + /** + * IPC interface that supports the app-facing {@link #TaskManager} api. + * {@hide} + */ +interface ITaskManager { + int schedule(in Task task); + void cancel(int taskId); + void cancelAll(); + List<Task> getAllPendingTasks(); +} diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/task/Task.aidl new file mode 100644 index 0000000..1f25439 --- /dev/null +++ b/core/java/android/app/task/Task.aidl @@ -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 android.app.task; + +parcelable Task; +
\ No newline at end of file diff --git a/core/java/android/content/Task.java b/core/java/android/app/task/Task.java index 407880f..ca4aeb2 100644 --- a/core/java/android/content/Task.java +++ b/core/java/android/app/task/Task.java @@ -14,23 +14,26 @@ * limitations under the License */ -package android.content; +package android.app.task; -import android.app.task.TaskService; +import android.content.ComponentName; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; /** - * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the + * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the * parameters required to schedule work against the calling application. These are constructed * using the {@link Task.Builder}. */ public class Task implements Parcelable { - public interface NetworkType { - public final int ANY = 0; - public final int UNMETERED = 1; + /** Default. */ + public final int NONE = 0; + /** This task requires network connectivity. */ + public final int ANY = 1; + /** This task requires network connectivity that is unmetered. */ + public final int UNMETERED = 2; } /** @@ -48,6 +51,8 @@ public class Task implements Parcelable { private final ComponentName service; private final boolean requireCharging; private final boolean requireDeviceIdle; + private final boolean hasEarlyConstraint; + private final boolean hasLateConstraint; private final int networkCapabilities; private final long minLatencyMillis; private final long maxExecutionDelayMillis; @@ -59,7 +64,7 @@ public class Task implements Parcelable { /** * Unique task id associated with this class. This is assigned to your task by the scheduler. */ - public int getTaskId() { + public int getId() { return taskId; } @@ -92,7 +97,7 @@ public class Task implements Parcelable { } /** - * See {@link android.content.Task.NetworkType} for a description of this value. + * See {@link android.app.task.Task.NetworkType} for a description of this value. */ public int getNetworkCapabilities() { return networkCapabilities; @@ -139,13 +144,31 @@ public class Task implements Parcelable { } /** - * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field + * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field * can take. This defaults to exponential. */ public int getBackoffPolicy() { return backoffPolicy; } + /** + * User can specify an early constraint of 0L, which is valid, so we keep track of whether the + * function was called at all. + * @hide + */ + public boolean hasEarlyConstraint() { + return hasEarlyConstraint; + } + + /** + * User can specify a late constraint of 0L, which is valid, so we keep track of whether the + * function was called at all. + * @hide + */ + public boolean hasLateConstraint() { + return hasLateConstraint; + } + private Task(Parcel in) { taskId = in.readInt(); extras = in.readBundle(); @@ -159,6 +182,8 @@ public class Task implements Parcelable { intervalMillis = in.readLong(); initialBackoffMillis = in.readLong(); backoffPolicy = in.readInt(); + hasEarlyConstraint = in.readInt() == 1; + hasLateConstraint = in.readInt() == 1; } private Task(Task.Builder b) { @@ -174,6 +199,8 @@ public class Task implements Parcelable { intervalMillis = b.mIntervalMillis; initialBackoffMillis = b.mInitialBackoffMillis; backoffPolicy = b.mBackoffPolicy; + hasEarlyConstraint = b.mHasEarlyConstraint; + hasLateConstraint = b.mHasLateConstraint; } @Override @@ -195,6 +222,8 @@ public class Task implements Parcelable { out.writeLong(intervalMillis); out.writeLong(initialBackoffMillis); out.writeInt(backoffPolicy); + out.writeInt(hasEarlyConstraint ? 1 : 0); + out.writeInt(hasLateConstraint ? 1 : 0); } public static final Creator<Task> CREATOR = new Creator<Task>() { @@ -212,7 +241,7 @@ public class Task implements Parcelable { /** * Builder class for constructing {@link Task} objects. */ - public final class Builder { + public static final class Builder { private int mTaskId; private Bundle mExtras; private ComponentName mTaskService; @@ -225,6 +254,8 @@ public class Task implements Parcelable { private long mMaxExecutionDelayMillis; // Periodic parameters. private boolean mIsPeriodic; + private boolean mHasEarlyConstraint; + private boolean mHasLateConstraint; private long mIntervalMillis; // Back-off parameters. private long mInitialBackoffMillis = 5000L; @@ -255,7 +286,7 @@ public class Task implements Parcelable { /** * Set some description of the kind of network capabilities you would like to have. This - * will be a parameter defined in {@link android.content.Task.NetworkType}. + * will be a parameter defined in {@link android.app.task.Task.NetworkType}. * Not calling this function means the network is not necessary. * Bear in mind that calling this function defines network as a strict requirement for your * task if the network requested is not available your task will never run. See @@ -307,6 +338,7 @@ public class Task implements Parcelable { public Builder setPeriodic(long intervalMillis) { mIsPeriodic = true; mIntervalMillis = intervalMillis; + mHasEarlyConstraint = mHasLateConstraint = true; return this; } @@ -314,12 +346,13 @@ public class Task implements Parcelable { * Specify that this task should be delayed by the provided amount of time. * Because it doesn't make sense setting this property on a periodic task, doing so will * throw an {@link java.lang.IllegalArgumentException} when - * {@link android.content.Task.Builder#build()} is called. + * {@link android.app.task.Task.Builder#build()} is called. * @param minLatencyMillis Milliseconds before which this task will not be considered for * execution. */ public Builder setMinimumLatency(long minLatencyMillis) { mMinLatencyMillis = minLatencyMillis; + mHasEarlyConstraint = true; return this; } @@ -328,10 +361,11 @@ public class Task implements Parcelable { * deadline even if other requirements are not met. Because it doesn't make sense setting * this property on a periodic task, doing so will throw an * {@link java.lang.IllegalArgumentException} when - * {@link android.content.Task.Builder#build()} is called. + * {@link android.app.task.Task.Builder#build()} is called. */ public Builder setOverrideDeadline(long maxExecutionDelayMillis) { mMaxExecutionDelayMillis = maxExecutionDelayMillis; + mHasLateConstraint = true; return this; } @@ -360,31 +394,18 @@ public class Task implements Parcelable { * @return The task object to hand to the TaskManager. This object is immutable. */ public Task build() { - // Check that extras bundle only contains primitive types. - try { - for (String key : extras.keySet()) { - Object value = extras.get(key); - if (value == null) continue; - if (value instanceof Long) continue; - if (value instanceof Integer) continue; - if (value instanceof Boolean) continue; - if (value instanceof Float) continue; - if (value instanceof Double) continue; - if (value instanceof String) continue; - throw new IllegalArgumentException("Unexpected value type: " - + value.getClass().getName()); - } - } catch (IllegalArgumentException e) { - throw e; - } catch (RuntimeException exc) { - throw new IllegalArgumentException("error unparcelling Bundle", exc); + if (mExtras == null) { + mExtras = Bundle.EMPTY; + } + if (mTaskId < 0) { + throw new IllegalArgumentException("Task id must be greater than 0."); } // Check that a deadline was not set on a periodic task. - if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) { + if (mIsPeriodic && mHasLateConstraint) { throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + "periodic task."); } - if (mIsPeriodic && (mMinLatencyMillis != 0L)) { + if (mIsPeriodic && mHasEarlyConstraint) { throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + "periodic task"); } diff --git a/core/java/android/content/TaskManager.java b/core/java/android/app/task/TaskManager.java index d28d78a..00f57da 100644 --- a/core/java/android/content/TaskManager.java +++ b/core/java/android/app/task/TaskManager.java @@ -14,14 +14,19 @@ * limitations under the License */ -package android.content; +package android.app.task; import java.util.List; +import android.content.Context; + /** * Class for scheduling various types of tasks with the scheduling framework on the device. * - * Get an instance of this class through {@link Context#getSystemService(String)}. + * <p>You do not + * instantiate this class directly; instead, retrieve it through + * {@link android.content.Context#getSystemService + * Context.getSystemService(Context.TASK_SERVICE)}. */ public abstract class TaskManager { /* @@ -29,18 +34,19 @@ public abstract class TaskManager { * if the run-time for your task is too short, or perhaps the system can't resolve the * requisite {@link TaskService} in your package. */ - static final int RESULT_INVALID_PARAMETERS = -1; + public static final int RESULT_FAILURE = 0; /** * Returned from {@link #schedule(Task)} if this application has made too many requests for * work over too short a time. */ // TODO: Determine if this is necessary. - static final int RESULT_OVER_QUOTA = -2; + public static final int RESULT_SUCCESS = 1; - /* - * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on - * the sorts of tasks you can schedule. - * @return If >0, this int corresponds to the taskId of the successfully scheduled task. + /** + * @param task The task you wish scheduled. See + * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks + * you can schedule. + * @return If >0, this int returns the taskId of the successfully scheduled task. * Otherwise you have to compare the return value to the error codes defined in this class. */ public abstract int schedule(Task task); diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java index 0351082..dacb348 100644 --- a/core/java/android/app/task/TaskParams.java +++ b/core/java/android/app/task/TaskParams.java @@ -47,7 +47,7 @@ public class TaskParams implements Parcelable { /** * @return The extras you passed in when constructing this task with - * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will + * {@link android.app.task.Task.Builder#setExtras(android.os.Bundle)}. This will * never be null. If you did not set any extras this will be an empty bundle. */ public Bundle getExtras() { diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java index ab1a565..8ce4484 100644 --- a/core/java/android/app/task/TaskService.java +++ b/core/java/android/app/task/TaskService.java @@ -28,7 +28,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; /** - * <p>Entry point for the callback from the {@link android.content.TaskManager}.</p> + * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p> * <p>This is the base class that handles asynchronous requests that were previously scheduled. You * are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where * you will implement your task logic.</p> @@ -215,9 +215,9 @@ public abstract class TaskService extends Service { * * <p>This will happen if the requirements specified at schedule time are no longer met. For * example you may have requested WiFi with - * {@link android.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your + * {@link android.app.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your * task was executing the user toggled WiFi. Another example is if you had specified - * {@link android.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its + * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its * idle maintenance window. You are solely responsible for the behaviour of your application * upon receipt of this message; your app will likely start to misbehave if you ignore it. One * immediate repercussion is that the system will cease holding a wakelock for you.</p> @@ -237,7 +237,7 @@ public abstract class TaskService extends Service { * You can specify post-execution behaviour to the scheduler here with * <code>needsReschedule </code>. This will apply a back-off timer to your task based on * the default, or what was set with - * {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The original + * {@link android.app.task.Task.Builder#setBackoffCriteria(long, int)}. The original * requirements are always honoured even for a backed-off task. Note that a task running in * idle mode will not be backed-off. Instead what will happen is the task will be re-added * to the queue and re-executed within a future idle maintenance window. diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6ae006c..b0673b5 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2023,6 +2023,7 @@ public abstract class Context { PRINT_SERVICE, MEDIA_SESSION_SERVICE, BATTERY_SERVICE, + TASK_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -2079,6 +2080,8 @@ public abstract class Context { * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads * <dt> {@link #BATTERY_SERVICE} ("batterymanager") * <dd> A {@link android.os.BatteryManager} for managing battery state + * <dt> {@link #TASK_SERVICE} ("taskmanager") + * <dd> A {@link android.app.task.TaskManager} for managing scheduled tasks * </dl> * * <p>Note: System services obtained via this API may be closely associated with @@ -2134,6 +2137,8 @@ public abstract class Context { * @see android.app.DownloadManager * @see #BATTERY_SERVICE * @see android.os.BatteryManager + * @see #TASK_SERVICE + * @see android.app.task.TaskManager */ public abstract Object getSystemService(@ServiceName @NonNull String name); @@ -2728,6 +2733,15 @@ public abstract class Context { public static final String USAGE_STATS_SERVICE = "usagestats"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.app.task.TaskManager} instance for managing occasional + * background tasks. + * @see #getSystemService + * @see android.app.task.TaskManager + */ + public static final String TASK_SERVICE = "task"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * 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..44a6a5d 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 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/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java index 220b40d..2dce425 100644 --- a/core/java/android/ddm/DdmHandleHello.java +++ b/core/java/android/ddm/DdmHandleHello.java @@ -22,6 +22,7 @@ import org.apache.harmony.dalvik.ddmc.DdmServer; import android.util.Log; import android.os.Debug; import android.os.UserHandle; +import dalvik.system.VMRuntime; import java.nio.ByteBuffer; @@ -126,8 +127,21 @@ public class DdmHandleHello extends ChunkHandler { // appName = "unknown"; String appName = DdmHandleAppName.getAppName(); - ByteBuffer out = ByteBuffer.allocate(20 - + vmIdent.length()*2 + appName.length()*2); + VMRuntime vmRuntime = VMRuntime.getRuntime(); + String instructionSetDescription = + vmRuntime.is64Bit() ? "64-bit" : "32-bit"; + String vmInstructionSet = vmRuntime.vmInstructionSet(); + if (vmInstructionSet != null && vmInstructionSet.length() > 0) { + instructionSetDescription += " (" + vmInstructionSet + ")"; + } + String vmFlags = "CheckJNI=" + + (vmRuntime.isCheckJniEnabled() ? "true" : "false"); + + ByteBuffer out = ByteBuffer.allocate(28 + + vmIdent.length() * 2 + + appName.length() * 2 + + instructionSetDescription.length() * 2 + + vmFlags.length() * 2); out.order(ChunkHandler.CHUNK_ORDER); out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION); out.putInt(android.os.Process.myPid()); @@ -136,6 +150,10 @@ public class DdmHandleHello extends ChunkHandler { putString(out, vmIdent); putString(out, appName); out.putInt(UserHandle.myUserId()); + out.putInt(instructionSetDescription.length()); + putString(out, instructionSetDescription); + out.putInt(vmFlags.length()); + putString(out, vmFlags); Chunk reply = new Chunk(CHUNK_HELO, out); 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 03b342c..accceb1 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 { @@ -424,6 +423,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/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java index ddaf870..62fa279 100644 --- a/core/java/android/hardware/hdmi/HdmiCecMessage.java +++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java @@ -46,7 +46,7 @@ public final class HdmiCecMessage implements Parcelable { public HdmiCecMessage(int source, int destination, int opcode, byte[] params) { mSource = source; mDestination = destination; - mOpcode = opcode; + mOpcode = opcode & 0xFF; mParams = Arrays.copyOf(params, params.length); } @@ -123,6 +123,7 @@ public final class HdmiCecMessage implements Parcelable { * @param p HdmiCecMessage object to read the Rating from * @return a new HdmiCecMessage created from the data in the parcel */ + @Override public HdmiCecMessage createFromParcel(Parcel p) { int source = p.readInt(); int destination = p.readInt(); @@ -131,6 +132,7 @@ public final class HdmiCecMessage implements Parcelable { p.readByteArray(params); return new HdmiCecMessage(source, destination, opcode, params); } + @Override public HdmiCecMessage[] newArray(int size) { return new HdmiCecMessage[size]; } @@ -139,11 +141,40 @@ public final class HdmiCecMessage implements Parcelable { @Override public String toString() { StringBuffer s = new StringBuffer(); - s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode)); - for (byte data : mParams) { - s.append(String.format("%02X ", data)); + s.append(String.format("<%s> src: %d, dst: %d", + opcodeToString(mOpcode), mSource, mDestination)); + if (mParams.length > 0) { + s.append(", params:"); + for (byte data : mParams) { + s.append(String.format(" %02X", data)); + } } return s.toString(); } + + private static String opcodeToString(int opcode) { + switch (opcode) { + case HdmiCec.MESSAGE_FEATURE_ABORT: + return "Feature Abort"; + case HdmiCec.MESSAGE_CEC_VERSION: + return "CEC Version"; + case HdmiCec.MESSAGE_REQUEST_ARC_INITIATION: + return "Request ARC Initiation"; + case HdmiCec.MESSAGE_REQUEST_ARC_TERMINATION: + return "Request ARC Termination"; + case HdmiCec.MESSAGE_REPORT_ARC_INITIATED: + return "Report ARC Initiated"; + case HdmiCec.MESSAGE_REPORT_ARC_TERMINATED: + return "Report ARC Terminated"; + case HdmiCec.MESSAGE_TEXT_VIEW_ON: + return "Text View On"; + case HdmiCec.MESSAGE_ACTIVE_SOURCE: + return "Active Source"; + case HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS: + return "Give Device Power Status"; + default: + return String.format("Opcode: %02X", opcode); + } + } } diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index e0d69e3..e489e05 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -16,10 +16,13 @@ package android.net; +import android.net.NetworkUtils; import android.os.Parcelable; import android.os.Parcel; +import java.io.IOException; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import javax.net.SocketFactory; @@ -38,6 +41,8 @@ public class Network implements Parcelable { */ public final int netId; + private NetworkBoundSocketFactory mNetworkBoundSocketFactory = null; + /** * @hide */ @@ -79,6 +84,59 @@ public class Network implements Parcelable { } /** + * A {@code SocketFactory} that produces {@code Socket}'s bound to this network. + */ + private class NetworkBoundSocketFactory extends SocketFactory { + private final int mNetId; + + public NetworkBoundSocketFactory(int netId) { + super(); + mNetId = netId; + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + Socket socket = createSocket(); + socket.bind(new InetSocketAddress(localHost, localPort)); + socket.connect(new InetSocketAddress(host, port)); + return socket; + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, + int localPort) throws IOException { + Socket socket = createSocket(); + socket.bind(new InetSocketAddress(localAddress, localPort)); + socket.connect(new InetSocketAddress(address, port)); + return socket; + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + Socket socket = createSocket(); + socket.connect(new InetSocketAddress(host, port)); + return socket; + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + Socket socket = createSocket(); + socket.connect(new InetSocketAddress(host, port)); + return socket; + } + + @Override + public Socket createSocket() throws IOException { + Socket socket = new Socket(); + // Query a property of the underlying socket to ensure the underlying + // socket exists so a file descriptor is available to bind to a network. + socket.getReuseAddress(); + NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId); + return socket; + } + } + + /** * Returns a {@link SocketFactory} bound to this network. Any {@link Socket} created by * this factory will have its traffic sent over this {@code Network}. Note that if this * {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the @@ -88,7 +146,10 @@ public class Network implements Parcelable { * {@code Network}. */ public SocketFactory socketFactory() { - return null; + if (mNetworkBoundSocketFactory == null) { + mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId); + } + return mNetworkBoundSocketFactory; } /** @@ -99,6 +160,29 @@ public class Network implements Parcelable { * doesn't accidentally use sockets it thinks are still bound to a particular {@code Network}. */ public void bindProcess() { + NetworkUtils.bindProcessToNetwork(netId); + } + + /** + * Binds host resolutions performed by this process to this network. {@link #bindProcess} + * takes precedence over this setting. + * + * @hide + * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature(). + */ + public void bindProcessForHostResolution() { + NetworkUtils.bindProcessToNetworkForHostResolution(netId); + } + + /** + * Clears any process specific {@link Network} binding for host resolution. This does + * not clear bindings enacted via {@link #bindProcess}. + * + * @hide + * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature(). + */ + public void unbindProcessForHostResolution() { + NetworkUtils.unbindProcessToNetworkForHostResolution(); } /** @@ -107,7 +191,7 @@ public class Network implements Parcelable { * @return {@code Network} to which this process is bound. */ public static Network getProcessBoundNetwork() { - return null; + return new Network(NetworkUtils.getNetworkBoundToProcess()); } /** @@ -115,6 +199,7 @@ public class Network implements Parcelable { * {@link Network#bindProcess}. */ public static void unbindProcess() { + NetworkUtils.unbindProcessToNetwork(); } // implement the Parcelable interface diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index c2b06a2..1c18ba5 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -257,31 +257,43 @@ public abstract class NetworkAgent extends Handler { } /** - * called to go through our list of requests and see if we're - * good enough to try connecting. + * 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. * - * Only does connects - we disconnect when requested via + * 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() { - if (mConnectionRequested) { - if (VDBG) log("evalScores - already trying - 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 lets connect! - mConnectionRequested = true; - connect(); + 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(); + } } } diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index b24d396..edb3286 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -109,6 +109,50 @@ public class NetworkUtils { public native static void markSocket(int socketfd, int mark); /** + * Binds the current process to the network designated by {@code netId}. All sockets created + * in the future (and not explicitly bound via a bound {@link SocketFactory} (see + * {@link Network#socketFactory}) will be bound to this network. Note that if this + * {@code Network} ever disconnects all sockets created in this way will cease to work. This + * is by design so an application doesn't accidentally use sockets it thinks are still bound to + * a particular {@code Network}. + */ + public native static void bindProcessToNetwork(int netId); + + /** + * Clear any process specific {@code Network} binding. This reverts a call to + * {@link #bindProcessToNetwork}. + */ + public native static void unbindProcessToNetwork(); + + /** + * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if + * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}. + */ + public native static int getNetworkBoundToProcess(); + + /** + * Binds host resolutions performed by this process to the network designated by {@code netId}. + * {@link #bindProcessToNetwork} takes precedence over this setting. + * + * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature(). + */ + public native static void bindProcessToNetworkForHostResolution(int netId); + + /** + * Clears any process specific {@link Network} binding for host resolution. This does + * not clear bindings enacted via {@link #bindProcessToNetwork}. + * + * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature(). + */ + public native static void unbindProcessToNetworkForHostResolution(); + + /** + * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This + * overrides any binding via {@link #bindProcessToNetwork}. + */ + public native static void bindSocketToNetwork(int socketfd, int netId); + + /** * Convert a IPv4 address from an integer to an InetAddress. * @param hostAddress an int corresponding to the IPv4 address in network byte order */ 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/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/view/View.java b/core/java/android/view/View.java index 025cf69..ce266d7 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 @@ -19760,6 +19784,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 fc7bf0e..c3bf295 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; @@ -1016,7 +1017,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 +2619,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 +3901,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 +4025,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 +5298,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 +5710,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 +5750,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 +5796,11 @@ public final class ViewRootImpl implements ViewParent, @Override public void onBatchedInputEventPending() { - scheduleConsumeBatchedInput(); + if (mUnbufferedInputDispatch) { + super.onBatchedInputEventPending(); + } else { + scheduleConsumeBatchedInput(); + } } @Override @@ -5784,6 +5821,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/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/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/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java index 495d5c6..df96488 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; @@ -211,54 +212,196 @@ 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; + + /** + * 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; } - // 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; + + public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, + InputMethodInfo imi, InputMethodSubtype subtype) { + if (imi == null) { + return null; } - // 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; - } + 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; + + public 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 { + // TODO: Switch to DynamicRotationList for smarter rotation. + private final StaticRotationList mSwitchingAwareSubtypeList; + private final StaticRotationList mSwitchingUnawareSubtypeList; + + public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) { + mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems, + true /* supportsSwitchingToNextInputMethod */)); + mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems, + false /* supportsSwitchingToNextInputMethod */)); + } + + public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi, + InputMethodSubtype subtype) { + if (imi == null) { + return null; + } + if (imi.supportsSwitchingToNextInputMethod()) { + return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi, + subtype); + } else { + return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi, + subtype); + } + } + + public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) { + if (imi == null) { + return; + } + // TODO: Enable the following code when DynamicRotationList is enabled. + // if (imi.supportsSwitchingToNextInputMethod()) { + // mSwitchingAwareSubtypeList.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 +412,30 @@ 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 = new ControllerImpl(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/jni/Android.mk b/core/jni/Android.mk index a159715..f446c3a 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -173,8 +173,10 @@ LOCAL_C_INCLUDES += \ $(call include-path-for, bluedroid) \ $(call include-path-for, libhardware)/hardware \ $(call include-path-for, libhardware_legacy)/hardware_legacy \ + $(TOP)/bionic/libc/dns/include \ $(TOP)/frameworks/av/include \ $(TOP)/system/media/camera/include \ + $(TOP)/system/netd/include \ external/pdfium/core/include/fpdfapi \ external/pdfium/core/include/fpdfdoc \ external/pdfium/fpdfsdk/include \ @@ -233,6 +235,7 @@ LOCAL_SHARED_LIBRARIES := \ libaudioutils \ libpdfium \ libimg_utils \ + libnetd_client \ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SHARED_LIBRARIES += libhwui 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/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_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_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 6d23c32..bc5e1b3 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -18,6 +18,8 @@ #include "jni.h" #include "JNIHelp.h" +#include "NetdClient.h" +#include "resolv_netid.h" #include <utils/misc.h> #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> @@ -250,6 +252,36 @@ static void android_net_utils_markSocket(JNIEnv *env, jobject thiz, jint socket, } } +static void android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId) +{ + setNetworkForProcess(netId); +} + +static void android_net_utils_unbindProcessToNetwork(JNIEnv *env, jobject thiz) +{ + setNetworkForProcess(NETID_UNSET); +} + +static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz) +{ + return getNetworkForProcess(); +} + +static void android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz, jint netId) +{ + setNetworkForResolv(netId); +} + +static void android_net_utils_unbindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz) +{ + setNetworkForResolv(NETID_UNSET); +} + +static void android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, jint netId) +{ + setNetworkForSocket(netId, socket); +} + // ---------------------------------------------------------------------------- /* @@ -267,6 +299,12 @@ static JNINativeMethod gNetworkUtilMethods[] = { { "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease }, { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError }, { "markSocket", "(II)V", (void*) android_net_utils_markSocket }, + { "bindProcessToNetwork", "(I)V", (void*) android_net_utils_bindProcessToNetwork }, + { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess }, + { "unbindProcessToNetwork", "()V", (void*) android_net_utils_unbindProcessToNetwork }, + { "bindProcessToNetworkForHostResolution", "(I)V", (void*) android_net_utils_bindProcessToNetworkForHostResolution }, + { "unbindProcessToNetworkForHostResolution", "()V", (void*) android_net_utils_unbindProcessToNetworkForHostResolution }, + { "bindSocketToNetwork", "(II)V", (void*) android_net_utils_bindSocketToNetwork }, }; int register_android_net_NetworkUtils(JNIEnv* env) 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/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/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..a7cd9a7 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -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/attrs.xml b/core/res/res/values/attrs.xml index c05dfca..9eb6f74 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4057,6 +4057,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" /> @@ -4065,6 +4068,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)}. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 7dc967c..42ed318 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2178,6 +2178,8 @@ <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-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index f6cd9e8..1f4c88d 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -231,14 +231,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/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/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.java new file mode 100644 index 0000000..d6883ec --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/src/com/android/multidexlegacyandexception/CaughtOnlyByIntermediateException.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 CaughtOnlyByIntermediateException extends RuntimeException { + +} 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..c0c20e2 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,211 @@ 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 = new ControllerImpl(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); } + // This test is disabled until DynamicRotationList is enabled. @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 DISABLED_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 = new ControllerImpl(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); } - } +} 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/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/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/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/FontFamily.java b/graphics/java/android/graphics/FontFamily.java index a759a79..210ea86b 100644 --- a/graphics/java/android/graphics/FontFamily.java +++ b/graphics/java/android/graphics/FontFamily.java @@ -31,7 +31,6 @@ public class FontFamily { public FontFamily() { mNativePtr = nCreateFamily(); - mNativePtr = nCreateFamily(); if (mNativePtr == 0) { throw new RuntimeException(); } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 64451c4..b7613fb 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -66,6 +66,9 @@ public class Typeface { static Map<String, Typeface> sSystemFontMap; static FontFamily[] sFallbackFonts; + static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml"; + static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml"; + /** * @hide */ @@ -249,10 +252,16 @@ public class Typeface { return fontFamily; } - static { + /* + * (non-Javadoc) + * + * This should only be called once, from the static class initializer block. + */ + private static void init() { // Load font config and initialize Minikin state - String systemConfigFilename = "/system/etc/system_fonts.xml"; - String configFilename = "/system/etc/fallback_fonts.xml"; + File systemFontConfigLocation = getSystemFontConfigLocation(); + File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG); + File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG); try { // TODO: throws an exception non-Minikin builds, to fail early; // remove when Minikin-only @@ -301,7 +310,10 @@ public class Typeface { } catch (XmlPullParserException e) { Log.e(TAG, "XML parse exception for " + configFilename); } + } + static { + init(); // Set up defaults and typefaces exposed in public API DEFAULT = create((String) null, 0); DEFAULT_BOLD = create((String) null, Typeface.BOLD); @@ -315,6 +327,11 @@ public class Typeface { create((String) null, Typeface.ITALIC), create((String) null, Typeface.BOLD_ITALIC), }; + + } + + private static File getSystemFontConfigLocation() { + return new File("/system/etc/"); } @Override diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 9a63fa3..492376f 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -58,9 +58,10 @@ 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.</dd> * <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> @@ -101,13 +102,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"; @@ -266,6 +267,7 @@ public class VectorDrawable extends Drawable { boolean noSizeTag = true; boolean noViewportTag = true; + boolean noGroupTag = true; boolean noPathTag = true; VGroup currentGroup = new VGroup(); @@ -285,12 +287,20 @@ public class VectorDrawable extends Drawable { } else if (SHAPE_VIEWPORT.equals(tagName)) { pathRenderer.parseViewport(res, attrs); noViewportTag = false; + } else if (SHAPE_GROUP.equals(tagName)) { + currentGroup = new VGroup(); + pathRenderer.mGroupList.add(currentGroup); + noGroupTag = false; } } eventType = parser.next(); } + if (noGroupTag && !noPathTag) { + pathRenderer.mGroupList.add(currentGroup); + } + if (noSizeTag || noViewportTag || noPathTag) { final StringBuffer tag = new StringBuffer(); @@ -315,7 +325,6 @@ public class VectorDrawable extends Drawable { throw new XmlPullParserException("no " + tag + " defined"); } - pathRenderer.mCurrentGroup = currentGroup; // post parse cleanup pathRenderer.parseFinish(); return pathRenderer; @@ -370,7 +379,7 @@ public class VectorDrawable extends Drawable { private ColorFilter mColorFilter; private PathMeasure mPathMeasure; - private VGroup mCurrentGroup = new VGroup(); + final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>(); float mBaseWidth = 0; float mBaseHeight = 0; @@ -381,7 +390,7 @@ public class VectorDrawable extends Drawable { } public VPathRenderer(VPathRenderer copy) { - mCurrentGroup = copy.mCurrentGroup; + mGroupList.addAll(copy.mGroupList); if (copy.mCurrentPaths != null) { mCurrentPaths = new VPath[copy.mCurrentPaths.length]; for (int i = 0; i < mCurrentPaths.length; i++) { @@ -396,22 +405,29 @@ public class VectorDrawable extends Drawable { } public boolean canApplyTheme() { - final ArrayList<VPath> paths = mCurrentGroup.mVGList; - for (int j = paths.size() - 1; j >= 0; j--) { - final VPath path = paths.get(j); - if (path.canApplyTheme()) { - return true; + final ArrayList<VGroup> groups = mGroupList; + for (int i = groups.size() - 1; i >= 0; i--) { + final ArrayList<VPath> paths = groups.get(i).mVGList; + for (int j = paths.size() - 1; j >= 0; j--) { + final VPath path = paths.get(j); + if (path.canApplyTheme()) { + return true; + } } } + return false; } public void applyTheme(Theme t) { - final ArrayList<VPath> paths = mCurrentGroup.mVGList; - for (int j = paths.size() - 1; j >= 0; j--) { - final VPath path = paths.get(j); - if (path.canApplyTheme()) { - path.applyTheme(t); + final ArrayList<VGroup> groups = mGroupList; + for (int i = groups.size() - 1; i >= 0; i--) { + final ArrayList<VPath> paths = groups.get(i).mVGList; + for (int j = paths.size() - 1; j >= 0; j--) { + final VPath path = paths.get(j); + if (path.canApplyTheme()) { + path.applyTheme(t); + } } } } @@ -426,6 +442,7 @@ public class VectorDrawable extends Drawable { if (mStrokePaint != null) { mStrokePaint.setColorFilter(colorFilter); } + } public void draw(Canvas canvas, int w, int h) { @@ -523,7 +540,7 @@ public class VectorDrawable extends Drawable { * TODO: improve memory use & performance or move to C++ */ public void parseFinish() { - final Collection<VPath> paths = mCurrentGroup.getPaths(); + final Collection<VPath> paths = mGroupList.get(0).getPaths(); mCurrentPaths = paths.toArray(new VPath[paths.size()]); for (int i = 0; i < mCurrentPaths.length; i++) { mCurrentPaths[i] = new VPath(mCurrentPaths[i]); 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/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/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java new file mode 100644 index 0000000..c088906 --- /dev/null +++ b/media/java/android/media/AudioDevicePort.java @@ -0,0 +1,85 @@ +/* + * 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.media; + +/** + * The AudioDevicePort is a specialized type of AudioPort + * describing an input (e.g microphone) or output device (e.g speaker) + * of the system. + * An AudioDevicePort is an AudioPort controlled by the audio HAL, almost always a physical + * device at the boundary of the audio system. + * In addition to base audio port attributes, the device descriptor contains: + * - the device type (e.g AudioManager.DEVICE_OUT_SPEAKER) + * - the device address (e.g MAC adddress for AD2P sink). + * @see AudioPort + * @hide + */ + +public class AudioDevicePort extends AudioPort { + + private final int mType; + private final String mAddress; + + AudioDevicePort(AudioHandle handle, int[] samplingRates, int[] channelMasks, + int[] formats, AudioGain[] gains, int type, String address) { + super(handle, + (AudioManager.isInputDevice(type) == true) ? + AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK, + samplingRates, channelMasks, formats, gains); + mType = type; + mAddress = address; + } + + /** + * Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER) + */ + public int type() { + return mType; + } + + /** + * Get the device address. Address format varies with the device type. + * - USB devices ({@link AudioManager#DEVICE_OUT_USB_DEVICE}, + * {@link AudioManager#DEVICE_IN_USB_DEVICE}) use an address composed of the ALSA card number + * and device number: "card=2;device=1" + * - Bluetooth devices ({@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO}, + * {@link AudioManager#DEVICE_OUT_BLUETOOTH_SCO}, {@link AudioManager#DEVICE_OUT_BLUETOOTH_A2DP}) + * use the MAC address of the bluetooth device in the form "00:11:22:AA:BB:CC" as reported by + * {@link BluetoothDevice#getAddress()}. + * - Deivces that do not have an address will indicate an empty string "". + */ + public String address() { + return mAddress; + } + + /** + * Build a specific configuration of this audio device port for use by methods + * like AudioManager.connectAudioPatch(). + */ + public AudioDevicePortConfig buildConfig(int samplingRate, int channelMask, int format, + AudioGainConfig gain) { + return new AudioDevicePortConfig(this, samplingRate, channelMask, format, gain); + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof AudioDevicePort)) { + return false; + } + return super.equals(o); + } +} diff --git a/media/java/android/media/AudioDevicePortConfig.java b/media/java/android/media/AudioDevicePortConfig.java new file mode 100644 index 0000000..a381e10 --- /dev/null +++ b/media/java/android/media/AudioDevicePortConfig.java @@ -0,0 +1,41 @@ +/* + * 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.media; + +/** + * An AudioDevicePortConfig describes a possible configuration of an output or input device + * (speaker, headphone, microphone ...). + * It is used to specify a sink or source when creating a connection with + * AudioManager.connectAudioPatch(). + * An AudioDevicePortConfig is obtained from AudioDevicePort.buildConfig(). + * @hide + */ + +public class AudioDevicePortConfig extends AudioPortConfig { + AudioDevicePortConfig(AudioDevicePort devicePort, int samplingRate, int channelMask, + int format, AudioGainConfig gain) { + super((AudioPort)devicePort, samplingRate, channelMask, format, gain); + } + + /** + * Returns the audio device port this AudioDevicePortConfig is issued from. + */ + public AudioDevicePort port() { + return (AudioDevicePort)mPort; + } +} + 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/AudioGain.java b/media/java/android/media/AudioGain.java new file mode 100644 index 0000000..57709d5 --- /dev/null +++ b/media/java/android/media/AudioGain.java @@ -0,0 +1,159 @@ +/* + * 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.media; + +/** + * The AudioGain describes a gain controller. Gain controllers are exposed by + * audio ports when the gain is configurable at this port's input or output. + * Gain values are expressed in millibels. + * A gain controller has the following attributes: + * - mode: defines modes of operation or features + * MODE_JOINT: all channel gains are controlled simultaneously + * MODE_CHANNELS: each channel gain is controlled individually + * MODE_RAMP: ramps can be applied when gain changes + * - channel mask: indicates for which channels the gain can be controlled + * - min value: minimum gain value in millibel + * - max value: maximum gain value in millibel + * - default value: gain value after reset in millibel + * - step value: granularity of gain control in millibel + * - min ramp duration: minimum ramp duration in milliseconds + * - max ramp duration: maximum ramp duration in milliseconds + * + * This object is always created by the framework and read only by applications. + * Applications get a list of AudioGainDescriptors from AudioPortDescriptor.gains() and can build a + * valid gain configuration from AudioGain.buildConfig() + * @hide + */ +public class AudioGain { + + /** + * Bit of AudioGain.mode() field indicating that + * all channel gains are controlled simultaneously + */ + public static final int MODE_JOINT = 1; + /** + * Bit of AudioGain.mode() field indicating that + * each channel gain is controlled individually + */ + public static final int MODE_CHANNELS = 2; + /** + * Bit of AudioGain.mode() field indicating that + * ramps can be applied when gain changes. The type of ramp (linear, log etc...) is + * implementation specific. + */ + public static final int MODE_RAMP = 4; + + private final int mIndex; + private final int mMode; + private final int mChannelMask; + private final int mMinValue; + private final int mMaxValue; + private final int mDefaultValue; + private final int mStepValue; + private final int mRampDurationMinMs; + private final int mRampDurationMaxMs; + + // The channel mask passed to the constructor is as specified in AudioFormat + // (e.g. AudioFormat.CHANNEL_OUT_STEREO) + AudioGain(int index, int mode, int channelMask, + int minValue, int maxValue, int defaultValue, int stepValue, + int rampDurationMinMs, int rampDurationMaxMs) { + mIndex = index; + mMode = mode; + mChannelMask = channelMask; + mMinValue = minValue; + mMaxValue = maxValue; + mDefaultValue = defaultValue; + mStepValue = stepValue; + mRampDurationMinMs = rampDurationMinMs; + mRampDurationMaxMs = rampDurationMaxMs; + } + + /** + * Bit field indicating supported modes of operation + */ + public int mode() { + return mMode; + } + + /** + * Indicates for which channels the gain can be controlled + * (e.g. AudioFormat.CHANNEL_OUT_STEREO) + */ + public int channelMask() { + return mChannelMask; + } + + /** + * Minimum gain value in millibel + */ + public int minValue() { + return mMinValue; + } + + /** + * Maximum gain value in millibel + */ + public int maxValue() { + return mMaxValue; + } + + /** + * Default gain value in millibel + */ + public int defaultValue() { + return mDefaultValue; + } + + /** + * Granularity of gain control in millibel + */ + public int stepValue() { + return mStepValue; + } + + /** + * Minimum ramp duration in milliseconds + * 0 if MODE_RAMP not set + */ + public int rampDurationMinMs() { + return mRampDurationMinMs; + } + + /** + * Maximum ramp duration in milliseconds + * 0 if MODE_RAMP not set + */ + public int rampDurationMaxMs() { + return mRampDurationMaxMs; + } + + /** + * Build a valid gain configuration for this gain controller for use by + * AudioPortDescriptor.setGain() + * @param mode: desired mode of operation + * @param channelMask: channels of which the gain should be modified. + * @param values: gain values for each channels. + * @param rampDurationMs: ramp duration if mode MODE_RAMP is set. + * ignored if MODE_JOINT. + */ + public AudioGainConfig buildConfig(int mode, int channelMask, + int[] values, int rampDurationMs) { + //TODO: check params here + return new AudioGainConfig(mIndex, this, mode, channelMask, values, rampDurationMs); + } +} diff --git a/media/java/android/media/AudioGainConfig.java b/media/java/android/media/AudioGainConfig.java new file mode 100644 index 0000000..ea61679 --- /dev/null +++ b/media/java/android/media/AudioGainConfig.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 android.media; + +/** + * The AudioGainConfig is used by APIs setting or getting values on a given gain + * controller. It contains a valid configuration (value, channels...) for a gain controller + * exposed by an audio port. + * @see AudioGain + * @see AudioPort + * @hide + */ +public class AudioGainConfig { + AudioGain mGain; + private final int mIndex; + private final int mMode; + private final int mChannelMask; + private final int mValues[]; + private final int mRampDurationMs; + + AudioGainConfig(int index, AudioGain gain, int mode, int channelMask, + int[] values, int rampDurationMs) { + mIndex = index; + mGain = gain; + mMode = mode; + mChannelMask = channelMask; + mValues = values; + mRampDurationMs = rampDurationMs; + } + + /** + * get the index of the parent gain. + * frameworks use only. + */ + int index() { + return mIndex; + } + + /** + * Bit field indicating requested modes of operation. See {@link AudioGain#MODE_JOINT}, + * {@link AudioGain#MODE_CHANNELS}, {@link AudioGain#MODE_RAMP} + */ + public int mode() { + return mMode; + } + + /** + * Indicates for which channels the gain is set. + * See {@link AudioFormat#CHANNEL_OUT_STEREO}, {@link AudioFormat#CHANNEL_OUT_MONO} ... + */ + public int channelMask() { + return mChannelMask; + } + + /** + * Gain values for each channel in the order of bits set in + * channelMask() from LSB to MSB + */ + public int[] values() { + return mValues; + } + + /** + * Ramp duration in milliseconds. N/A if mode() does not + * specify MODE_RAMP. + */ + public int rampDurationMs() { + return mRampDurationMs; + } +} diff --git a/media/java/android/media/AudioHandle.java b/media/java/android/media/AudioHandle.java new file mode 100644 index 0000000..b58e7a3 --- /dev/null +++ b/media/java/android/media/AudioHandle.java @@ -0,0 +1,49 @@ +/* + * 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.media; + +/** + * The AudioHandle is used by the audio framework implementation to + * uniquely identify a particular component of the routing topology + * (AudioPort or AudioPatch) + * It is not visible or used at the API. + */ +class AudioHandle { + private final int mId; + + AudioHandle(int id) { + mId = id; + } + + int id() { + return mId; + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof AudioHandle)) { + return false; + } + AudioHandle ah = (AudioHandle)o; + return mId == ah.id(); + } + + @Override + public int hashCode() { + return mId; + } +} diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 9803161..f4affa0 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -39,6 +39,7 @@ import android.util.Log; import android.view.KeyEvent; import java.util.HashMap; +import java.util.ArrayList; /** * AudioManager provides access to volume and ringer mode control. @@ -2966,4 +2967,151 @@ public class AudioManager { Log.w(TAG, "Error disabling safe media volume", e); } } + + /** + * Return codes for listAudioPorts(), createAudioPatch() ... + */ + + /** @hide + */ + public static final int SUCCESS = AudioSystem.SUCCESS; + /** @hide + */ + public static final int ERROR = AudioSystem.ERROR; + /** @hide + */ + public static final int ERROR_BAD_VALUE = AudioSystem.BAD_VALUE; + /** @hide + */ + public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION; + /** @hide + */ + public static final int ERROR_PERMISSION_DENIED = AudioSystem.PERMISSION_DENIED; + /** @hide + */ + public static final int ERROR_NO_INIT = AudioSystem.NO_INIT; + /** @hide + */ + public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT; + + /** + * Returns a list of descriptors for all audio ports managed by the audio framework. + * Audio ports are nodes in the audio framework or audio hardware that can be configured + * or connected and disconnected with createAudioPatch() or releaseAudioPatch(). + * See AudioPort for a list of attributes of each audio port. + * @param ports An AudioPort ArrayList where the list will be returned. + * @hide + */ + public int listAudioPorts(ArrayList<AudioPort> ports) { + return ERROR_INVALID_OPERATION; + } + + /** + * Specialized version of listAudioPorts() listing only audio devices (AudioDevicePort) + * @see listAudioPorts(ArrayList<AudioPort>) + * @hide + */ + public int listAudioDevicePorts(ArrayList<AudioPort> devices) { + return ERROR_INVALID_OPERATION; + } + + /** + * Create a connection between two or more devices. The framework will reject the request if + * device types are not compatible or the implementation does not support the requested + * configuration. + * NOTE: current implementation is limited to one source and one sink per patch. + * @param patch AudioPatch array where the newly created patch will be returned. + * As input, if patch[0] is not null, the specified patch will be replaced by the + * new patch created. This avoids calling releaseAudioPatch() when modifying a + * patch and allows the implementation to optimize transitions. + * @param sources List of source audio ports. All must be AudioPort.ROLE_SOURCE. + * @param sinks List of sink audio ports. All must be AudioPort.ROLE_SINK. + * + * @return - {@link #SUCCESS} if connection is successful. + * - {@link #ERROR_BAD_VALUE} if incompatible device types are passed. + * - {@link #ERROR_INVALID_OPERATION} if the requested connection is not supported. + * - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to create + * a patch. + * - {@link #ERROR_DEAD_OBJECT} if the server process is dead + * - {@link #ERROR} if patch cannot be connected for any other reason. + * + * patch[0] contains the newly created patch + * @hide + */ + public int createAudioPatch(AudioPatch[] patch, + AudioPortConfig[] sources, + AudioPortConfig[] sinks) { + return ERROR_INVALID_OPERATION; + } + + /** + * Releases an existing audio patch connection. + * @param patch The audio patch to disconnect. + * @return - {@link #SUCCESS} if disconnection is successful. + * - {@link #ERROR_BAD_VALUE} if the specified patch does not exist. + * - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to release + * a patch. + * - {@link #ERROR_DEAD_OBJECT} if the server process is dead + * - {@link #ERROR} if patch cannot be released for any other reason. + * @hide + */ + public int releaseAudioPatch(AudioPatch patch) { + return ERROR_INVALID_OPERATION; + } + + /** + * List all existing connections between audio ports. + * @param patches An AudioPatch array where the list will be returned. + * @hide + */ + public int listAudioPatches(ArrayList<AudioPatch> patches) { + return ERROR_INVALID_OPERATION; + } + + /** + * Set the gain on the specified AudioPort. The AudioGainConfig config is build by + * AudioGain.buildConfig() + * @hide + */ + public int setAudioPortGain(AudioPort port, AudioGainConfig gain) { + return ERROR_INVALID_OPERATION; + } + + /** + * Listener registered by client to be notified upon new audio port connections, + * disconnections or attributes update. + * @hide + */ + public interface OnAudioPortUpdateListener { + /** + * Callback method called upon audio port list update. + * @param portList the updated list of audio ports + */ + public void OnAudioPortListUpdate(AudioPort[] portList); + + /** + * Callback method called upon audio patch list update. + * @param patchList the updated list of audio patches + */ + public void OnAudioPatchListUpdate(AudioPatch[] patchList); + + /** + * Callback method called when the mediaserver dies + */ + public void OnServiceDied(); + } + + /** + * Register an audio port update listener. + * @hide + */ + public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) { + } + + /** + * Unregister an audio port update listener. + * @hide + */ + public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) { + } } diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java new file mode 100644 index 0000000..1500a43 --- /dev/null +++ b/media/java/android/media/AudioMixPort.java @@ -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. + */ + +package android.media; + +/** + * The AudioMixPort is a specialized type of AudioPort + * describing an audio mix or stream at an input or output stream of the audio + * framework. + * @see AudioPort + * @hide + */ + +public class AudioMixPort extends AudioPort { + + AudioMixPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks, + int[] formats, AudioGain[] gains) { + super(handle, role, samplingRates, channelMasks, formats, gains); + } + + /** + * Build a specific configuration of this audio mix port for use by methods + * like AudioManager.connectAudioPatch(). + */ + public AudioMixPortConfig buildConfig(int samplingRate, int channelMask, int format, + AudioGainConfig gain) { + return new AudioMixPortConfig(this, samplingRate, channelMask, format, gain); + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof AudioMixPort)) { + return false; + } + return super.equals(o); + } + +} diff --git a/media/java/android/media/AudioMixPortConfig.java b/media/java/android/media/AudioMixPortConfig.java new file mode 100644 index 0000000..8eb9ef4 --- /dev/null +++ b/media/java/android/media/AudioMixPortConfig.java @@ -0,0 +1,41 @@ +/* + * 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.media; + +/** + * An AudioMixPortConfig describes a possible configuration of an output or input mixer. + * It is used to specify a sink or source when creating a connection with + * AudioManager.connectAudioPatch(). + * An AudioMixPortConfig is obtained from AudioMixPort.buildConfig(). + * @hide + */ + +public class AudioMixPortConfig extends AudioPortConfig { + + AudioMixPortConfig(AudioMixPort mixPort, int samplingRate, int channelMask, int format, + AudioGainConfig gain) { + super((AudioPort)mixPort, samplingRate, channelMask, format, gain); + } + + /** + * Returns the audio mix port this AudioMixPortConfig is issued from. + */ + public AudioMixPort port() { + return (AudioMixPort)mPort; + } +} + diff --git a/media/java/android/media/AudioPatch.java b/media/java/android/media/AudioPatch.java new file mode 100644 index 0000000..72291f6 --- /dev/null +++ b/media/java/android/media/AudioPatch.java @@ -0,0 +1,55 @@ +/* + * 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.media; + + +/** + * An AudioPatch describes a connection between audio sources and audio sinks. + * An audio source can be an output mix (playback AudioBus) or an input device (microphone). + * An audio sink can be an output device (speaker) or an input mix (capture AudioBus). + * An AudioPatch is created by AudioManager.connectAudioPatch() and released by + * AudioManager.disconnectAudioPatch() + * It contains the list of source and sink AudioPortConfig showing audio port configurations + * being connected. + * @hide + */ +public class AudioPatch { + + private final AudioHandle mHandle; + private final AudioPortConfig[] mSources; + private final AudioPortConfig[] mSinks; + + AudioPatch(AudioHandle patchHandle, AudioPortConfig[] sources, AudioPortConfig[] sinks) { + mHandle = patchHandle; + mSources = sources; + mSinks = sinks; + } + + /** + * Retrieve the list of sources of this audio patch. + */ + public AudioPortConfig[] sources() { + return mSources; + } + + /** + * Retreive the list of sinks of this audio patch. + */ + public AudioPortConfig[] sinks() { + return mSinks; + } +} diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java new file mode 100644 index 0000000..9aeddef --- /dev/null +++ b/media/java/android/media/AudioPort.java @@ -0,0 +1,174 @@ +/* + * 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.media; + + +/** + * An audio port is a node of the audio framework or hardware that can be connected to or + * disconnect from another audio node to create a specific audio routing configuration. + * Examples of audio ports are an output device (speaker) or an output mix (see AudioMixPort). + * All attributes that are relevant for applications to make routing selection are decribed + * in an AudioPort, in particular: + * - possible channel mask configurations. + * - audio format (PCM 16bit, PCM 24bit...) + * - gain: a port can be associated with one or more gain controllers (see AudioGain). + * + * This object is always created by the framework and read only by applications. + * A list of all audio port descriptors currently available for applications to control + * is obtained by AudioManager.listAudioPorts(). + * An application can obtain an AudioPortConfig for a valid configuration of this port + * by calling AudioPort.buildConfig() and use this configuration + * to create a connection between audio sinks and sources with AudioManager.connectAudioPatch() + * + * @hide + */ +public class AudioPort { + + /** + * For use by the audio framework. + */ + public static final int ROLE_NONE = 0; + /** + * The audio port is a source (produces audio) + */ + public static final int ROLE_SOURCE = 1; + /** + * The audio port is a sink (consumes audio) + */ + public static final int ROLE_SINK = 2; + + /** + * audio port type for use by audio framework implementation + */ + public static final int TYPE_NONE = 0; + /** + */ + public static final int TYPE_DEVICE = 1; + /** + */ + public static final int TYPE_SUBMIX = 2; + /** + */ + public static final int TYPE_SESSION = 3; + + + AudioHandle mHandle; + private final int mRole; + private final int[] mSamplingRates; + private final int[] mChannelMasks; + private final int[] mFormats; + private final AudioGain[] mGains; + private AudioPortConfig mActiveConfig; + + AudioPort(AudioHandle handle, int role, int[] samplingRates, int[] channelMasks, + int[] formats, AudioGain[] gains) { + mHandle = handle; + mRole = role; + mSamplingRates = samplingRates; + mChannelMasks = channelMasks; + mFormats = formats; + mGains = gains; + } + + AudioHandle handle() { + return mHandle; + } + + /** + * Get the audio port role + */ + public int role() { + return mRole; + } + + /** + * Get the list of supported sampling rates + * Empty array if sampling rate is not relevant for this audio port + */ + public int[] samplingRates() { + return mSamplingRates; + } + + /** + * Get the list of supported channel mask configurations + * (e.g AudioFormat.CHANNEL_OUT_STEREO) + * Empty array if channel mask is not relevant for this audio port + */ + public int[] channelMasks() { + return mChannelMasks; + } + + /** + * Get the list of supported audio format configurations + * (e.g AudioFormat.ENCODING_PCM_16BIT) + * Empty array if format is not relevant for this audio port + */ + public int[] formats() { + return mFormats; + } + + /** + * Get the list of gain descriptors + * Empty array if this port does not have gain control + */ + public AudioGain[] gains() { + return mGains; + } + + /** + * Get the gain descriptor at a given index + */ + AudioGain gain(int index) { + return mGains[index]; + } + + /** + * Build a specific configuration of this audio port for use by methods + * like AudioManager.connectAudioPatch(). + * @param channelMask The desired channel mask. AudioFormat.CHANNEL_OUT_DEFAULT if no change + * from active configuration requested. + * @param format The desired audio format. AudioFormat.ENCODING_DEFAULT if no change + * from active configuration requested. + * @param gain The desired gain. null if no gain changed requested. + */ + public AudioPortConfig buildConfig(int samplingRate, int channelMask, int format, + AudioGainConfig gain) { + return new AudioPortConfig(this, samplingRate, channelMask, format, gain); + } + + /** + * Get currently active configuration of this audio port. + */ + public AudioPortConfig activeConfig() { + return mActiveConfig; + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof AudioPort)) { + return false; + } + AudioPort ap = (AudioPort)o; + return mHandle.equals(ap.handle()); + } + + @Override + public int hashCode() { + return mHandle.hashCode(); + } +} + diff --git a/media/java/android/media/AudioPortConfig.java b/media/java/android/media/AudioPortConfig.java new file mode 100644 index 0000000..5dc768d --- /dev/null +++ b/media/java/android/media/AudioPortConfig.java @@ -0,0 +1,93 @@ +/* + * 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.media; + +/** + * An AudioPortConfig contains a possible configuration of an audio port chosen + * among all possible attributes described by an AudioPort. + * An AudioPortConfig is created by AudioPort.buildConfiguration(). + * AudioPorts are used to specify the sources and sinks of a patch created + * with AudioManager.connectAudioPatch(). + * Several specialized versions of AudioPortConfig exist to handle different categories of + * audio ports and their specific attributes: + * - AudioDevicePortConfig for input (e.g micropohone) and output devices (e.g speaker) + * - AudioMixPortConfig for input or output streams of the audio framework. + * @hide + */ + +public class AudioPortConfig { + final AudioPort mPort; + private final int mSamplingRate; + private final int mChannelMask; + private final int mFormat; + private final AudioGainConfig mGain; + + // mConfigMask indicates which fields in this configuration should be + // taken into account. Used with AudioSystem.setAudioPortConfig() + // framework use only. + static final int SAMPLE_RATE = 0x1; + static final int CHANNEL_MASK = 0x2; + static final int FORMAT = 0x4; + static final int GAIN = 0x8; + int mConfigMask; + + AudioPortConfig(AudioPort port, int samplingRate, int channelMask, int format, + AudioGainConfig gain) { + mPort = port; + mSamplingRate = samplingRate; + mChannelMask = channelMask; + mFormat = format; + mGain = gain; + mConfigMask = 0; + } + + /** + * Returns the audio port this AudioPortConfig is issued from. + */ + public AudioPort port() { + return mPort; + } + + /** + * Sampling rate configured for this AudioPortConfig. + */ + public int samplingRate() { + return mSamplingRate; + } + + /** + * Channel mask configuration (e.g AudioFormat.CHANNEL_CONFIGURATION_STEREO). + */ + public int channelMask() { + return mChannelMask; + } + + /** + * Audio format configuration (e.g AudioFormat.ENCODING_PCM_16BIT). + */ + public int format() { + return mFormat; + } + + /** + * The gain configuration if this port supports gain control, null otherwise + * @see AudioGainConfig. + */ + public AudioGainConfig gain() { + return mGain; + } +} diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 5b620fd..cf18902 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -2916,54 +2916,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)); + } } } } @@ -2981,32 +2983,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); } } } @@ -3016,94 +3020,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() { 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/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/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/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-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-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/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index d48e627..d26cfae 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -223,8 +223,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 23bcea2..c93fbf6 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -225,8 +225,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 02313b3..98373c9 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -202,15 +202,11 @@ <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="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 +223,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index 506ec3d..409b9ba 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -202,7 +202,7 @@ <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> @@ -223,8 +223,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index 09e52cc..82216de 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -223,8 +223,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 079c8f0..b0e58e4 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -204,15 +204,11 @@ <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) --> - <skip /> + <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> <string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do aplicativo"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string> @@ -229,8 +225,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 10afe88..c48733f 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -223,8 +223,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1d27854..3525b5e 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -221,7 +221,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 306416b..3217daa 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -223,7 +223,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index debef15..2f8ae2f 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -202,15 +202,11 @@ <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) --> - <skip /> + <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> <string name="recents_empty_message" msgid="2269156590813544104">"MGA KAMAKAILAN"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string> @@ -227,8 +223,7 @@ <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> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index c6da6ac..d8e1c35 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -225,8 +225,7 @@ <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> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 6246e05..bf85a07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -182,6 +182,7 @@ public abstract class BaseStatusBar extends SystemUI implements */ protected int mState; protected boolean mBouncerShowing; + protected boolean mShowLockscreenNotifications; protected NotificationOverflowContainer mKeyguardIconOverflowContainer; @@ -201,6 +202,9 @@ public abstract class BaseStatusBar extends SystemUI implements final int mode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF); setZenMode(mode); + final boolean show = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1) != 0; + setShowLockscreenNotifications(show); } }; @@ -362,6 +366,9 @@ public abstract class BaseStatusBar extends SystemUI implements mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false, mSettingsObserver); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, + mSettingsObserver); mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), @@ -1247,6 +1254,10 @@ public abstract class BaseStatusBar extends SystemUI implements updateNotifications(); } + protected void setShowLockscreenNotifications(boolean show) { + mShowLockscreenNotifications = show; + } + protected abstract void haltTicker(); protected abstract void setAreThereNotifications(); protected abstract void updateNotifications(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 5dcd61c..0e5b7e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -505,6 +505,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override + protected void setShowLockscreenNotifications(boolean show) { + super.setShowLockscreenNotifications(show); + updateStackScrollerState(); + } + + @Override public void start() { mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); @@ -2851,7 +2857,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void updateStackScrollerState() { + if (mStackScroller == null) return; mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */); + mStackScroller.setVisibility(!mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD + ? View.INVISIBLE : View.VISIBLE); } public void userActivity() { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index e178773..3c37902 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -66,6 +66,7 @@ import android.os.Vibrator; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; +import android.telephony.TelephonyManager; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -1925,9 +1926,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { ServiceManager.checkService(DreamService.DREAM_SERVICE)); } - static ITelephony getTelephonyService() { - return ITelephony.Stub.asInterface( - ServiceManager.checkService(Context.TELEPHONY_SERVICE)); + TelephonyManager getTelephonyService() { + return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); } static IAudioService getAudioService() { @@ -2010,14 +2010,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If an incoming call is ringing, HOME is totally disabled. // (The user is already on the InCallScreen at this point, // and his ONLY options are to answer or reject the call.) - try { - ITelephony telephonyService = getTelephonyService(); - if (telephonyService != null && telephonyService.isRinging()) { - Log.i(TAG, "Ignoring HOME; there's a ringing incoming call."); - return -1; - } - } catch (RemoteException ex) { - Log.w(TAG, "RemoteException from getPhoneInterface()", ex); + TelephonyManager telephonyManager = getTelephonyService(); + if (telephonyManager != null && telephonyManager.isRinging()) { + Log.i(TAG, "Ignoring HOME; there's a ringing incoming call."); + return -1; } // Delay handling home if a double-tap is possible. @@ -3967,37 +3963,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } if (down) { - ITelephony telephonyService = getTelephonyService(); - if (telephonyService != null) { - try { - if (telephonyService.isRinging()) { - // If an incoming call is ringing, either VOLUME key means - // "silence ringer". We handle these keys here, rather than - // in the InCallScreen, to make sure we'll respond to them - // even if the InCallScreen hasn't come to the foreground yet. - // Look for the DOWN event here, to agree with the "fallback" - // behavior in the InCallScreen. - Log.i(TAG, "interceptKeyBeforeQueueing:" - + " VOLUME key-down while ringing: Silence ringer!"); - - // Silence the ringer. (It's safe to call this - // even if the ringer has already been silenced.) - telephonyService.silenceRinger(); - - // And *don't* pass this key thru to the current activity - // (which is probably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; - break; - } - if (telephonyService.isOffhook() - && (result & ACTION_PASS_TO_USER) == 0) { - // If we are in call but we decided not to pass the key to - // the application, handle the volume change here. - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); - break; - } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); + TelephonyManager telephonyManager = getTelephonyService(); + if (telephonyManager != null) { + if (telephonyManager.isRinging()) { + // If an incoming call is ringing, either VOLUME key means + // "silence ringer". We handle these keys here, rather than + // in the InCallScreen, to make sure we'll respond to them + // even if the InCallScreen hasn't come to the foreground yet. + // Look for the DOWN event here, to agree with the "fallback" + // behavior in the InCallScreen. + Log.i(TAG, "interceptKeyBeforeQueueing:" + + " VOLUME key-down while ringing: Silence ringer!"); + + // Silence the ringer. (It's safe to call this + // even if the ringer has already been silenced.) + telephonyManager.silenceRinger(); + + // And *don't* pass this key thru to the current activity + // (which is probably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; + break; + } + if (telephonyManager.isOffhook() + && (result & ACTION_PASS_TO_USER) == 0) { + // If we are in call but we decided not to pass the key to + // the application, handle the volume change here. + handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); + break; } } @@ -4014,14 +4006,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_ENDCALL: { result &= ~ACTION_PASS_TO_USER; if (down) { - ITelephony telephonyService = getTelephonyService(); + TelephonyManager telephonyManager = getTelephonyService(); boolean hungUp = false; - if (telephonyService != null) { - try { - hungUp = telephonyService.endCall(); - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); - } + if (telephonyManager != null) { + hungUp = telephonyManager.endCall(); } interceptPowerKeyDown(!interactive || hungUp); } else { @@ -4057,23 +4045,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { interceptScreenshotChord(); } - ITelephony telephonyService = getTelephonyService(); + TelephonyManager telephonyManager = getTelephonyService(); boolean hungUp = false; - if (telephonyService != null) { - try { - if (telephonyService.isRinging()) { - // Pressing Power while there's a ringing incoming - // call should silence the ringer. - telephonyService.silenceRinger(); - } else if ((mIncallPowerBehavior - & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 - && telephonyService.isOffhook() && interactive) { - // Otherwise, if "Power button ends call" is enabled, - // the Power button will hang up any current active call. - hungUp = telephonyService.endCall(); - } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); + if (telephonyManager != null) { + if (telephonyManager.isRinging()) { + // Pressing Power while there's a ringing incoming + // call should silence the ringer. + telephonyManager.silenceRinger(); + } else if ((mIncallPowerBehavior + & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 + && telephonyManager.isOffhook() && interactive) { + // Otherwise, if "Power button ends call" is enabled, + // the Power button will hang up any current active call. + hungUp = telephonyManager.endCall(); } } interceptPowerKeyDown(!interactive || hungUp @@ -4106,16 +4090,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: if (down) { - ITelephony telephonyService = getTelephonyService(); - if (telephonyService != null) { - try { - if (!telephonyService.isIdle()) { - // Suppress PLAY/PAUSE toggle when phone is ringing or in-call - // to avoid music playback. - break; - } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); + TelephonyManager telephonyManager = getTelephonyService(); + if (telephonyManager != null) { + if (!telephonyManager.isIdle()) { + // Suppress PLAY/PAUSE toggle when phone is ringing or in-call + // to avoid music playback. + break; } } } @@ -4145,20 +4125,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_CALL: { if (down) { - ITelephony telephonyService = getTelephonyService(); - if (telephonyService != null) { - try { - if (telephonyService.isRinging()) { - Log.i(TAG, "interceptKeyBeforeQueueing:" - + " CALL key-down while ringing: Answer the call!"); - telephonyService.answerRingingCall(); - - // And *don't* pass this key thru to the current activity - // (which is presumably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; - } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException", ex); + TelephonyManager telephonyManager = getTelephonyService(); + if (telephonyManager != null) { + if (telephonyManager.isRinging()) { + Log.i(TAG, "interceptKeyBeforeQueueing:" + + " CALL key-down while ringing: Answer the call!"); + telephonyManager.answerRingingCall(); + + // And *don't* pass this key thru to the current activity + // (which is presumably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; } } } @@ -5575,8 +5551,13 @@ 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); + mStatusBarController.dump(pw, prefix); mNavigationBarController.dump(pw, prefix); PolicyControl.dump(prefix, pw); + + if (mOrientationListener != null) { + mOrientationListener.dump(pw, prefix); + } } } 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/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index fc808ec..ed83b4b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -283,7 +283,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; @@ -6016,6 +6016,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; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index ae7fab3..92f41f7 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -48,6 +48,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; @@ -76,6 +77,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; @@ -91,11 +93,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; @@ -131,9 +135,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; @@ -252,6 +262,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; @@ -2949,9 +2974,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)) { @@ -2961,6 +2989,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) { @@ -3057,6 +3087,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/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/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java index 1bc278d..0ba7773 100644 --- a/services/core/java/com/android/server/hdmi/FeatureAction.java +++ b/services/core/java/com/android/server/hdmi/FeatureAction.java @@ -133,7 +133,8 @@ abstract class FeatureAction { @Override public void sendTimerMessage(int state, long delayMillis) { - sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state), delayMillis); + // The third argument(0) is not used. + sendMessageDelayed(obtainMessage(MSG_TIMEOUT, state, 0), delayMillis); } @Override @@ -164,8 +165,13 @@ abstract class FeatureAction { mActionTimer.sendTimerMessage(state, delayMillis); } - protected final boolean sendCommand(HdmiCecMessage cmd) { - return mService.sendCecCommand(cmd); + protected final void sendCommand(HdmiCecMessage cmd) { + mService.sendCecCommand(cmd); + } + + protected final void sendCommand(HdmiCecMessage cmd, + HdmiControlService.SendMessageCallback callback) { + mService.sendCecCommand(cmd, callback); } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index f99a01d..3c18a59 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -21,10 +21,10 @@ import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.Looper; -import android.os.Message; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseIntArray; + +import com.android.server.hdmi.HdmiControlService.DevicePollingCallback; import libcore.util.EmptyArray; @@ -37,6 +37,9 @@ import java.util.List; * and pass it to CEC HAL so that it sends message to other device. For incoming * message it translates the message and delegates it to proper module. * + * <p>It should be careful to access member variables on IO thread because + * it can be accessed from system thread as well. + * * <p>It can be created only by {@link HdmiCecController#create} * * <p>Declared as package-private, accessed by {@link HdmiControlService} only. @@ -58,8 +61,7 @@ final class HdmiCecController { private static final int NUM_LOGICAL_ADDRESS = 16; - // TODO: define other constants for errors. - private static final int ERROR_SUCCESS = 0; + private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3; // Handler instance to process synchronous I/O (mainly send) message. private Handler mIoHandler; @@ -70,22 +72,19 @@ final class HdmiCecController { // Stores the pointer to the native implementation of the service that // interacts with HAL. - private long mNativePtr; + private volatile long mNativePtr; private HdmiControlService mService; - // Map-like container of all cec devices. A logical address of device is - // used as key of container. - private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = - new SparseArray<HdmiCecDeviceInfo>(); - // Set-like container for all local devices' logical address. - // Key and value are same. - private final SparseIntArray mLocalAddresses = new SparseIntArray(); + // Map-like container of all cec devices including local ones. + // A logical address of device is used as key of container. + private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>(); + + // Stores the local CEC devices in the system. Device type is used for key. + private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>(); // Private constructor. Use HdmiCecController.create(). private HdmiCecController() { - // TODO: Consider restoring the local device addresses from persistent storage - // to allocate the same addresses again if possible. } /** @@ -99,53 +98,42 @@ final class HdmiCecController { * returns {@code null}. */ static HdmiCecController create(HdmiControlService service) { - HdmiCecController handler = new HdmiCecController(); - long nativePtr = nativeInit(handler); + HdmiCecController controller = new HdmiCecController(); + long nativePtr = nativeInit(controller); if (nativePtr == 0L) { - handler = null; + controller = null; return null; } - handler.init(service, nativePtr); - return handler; + controller.init(service, nativePtr); + return controller; } - /** - * Initialize {@link #mLocalAddresses} by allocating logical addresses for each hosted type. - * - * @param deviceTypes local device types - */ - void initializeLocalDevices(int[] deviceTypes) { - for (int deviceType : deviceTypes) { - int preferred = getPreferredAddress(deviceType); - allocateLogicalAddress(deviceType, preferred, new AllocateLogicalAddressCallback() { - @Override - public void onAllocated(int deviceType, int logicalAddress) { - addLogicalAddress(logicalAddress); - } - }); - } + private void init(HdmiControlService service, long nativePtr) { + mService = service; + mIoHandler = new Handler(service.getServiceLooper()); + mControlHandler = new Handler(service.getServiceLooper()); + mNativePtr = nativePtr; } /** - * Get the preferred address for a given type. + * Perform initialization for each hosted device. * - * @param deviceType logical device type to get the address for - * @return preferred address; {@link HdmiCec#ADDR_UNREGISTERED} if not available. + * @param deviceTypes array of device types */ - private int getPreferredAddress(int deviceType) { - // Uses the data restored from persistent memory at boot up if they are available. - // Otherwise we return UNREGISTERED indicating there is no preferred address. - // Note that for address SPECIFIC_USE(14), HdmiCec.getTypeFromAddress() returns DEVICE_TV, - // meaning that we do not support device type video processor yet. - for (int i = 0; i < mLocalAddresses.size(); ++i) { - int address = mLocalAddresses.keyAt(i); - int type = HdmiCec.getTypeFromAddress(address); - if (type == deviceType) { - return address; + void initializeLocalDevices(int[] deviceTypes) { + assertRunOnServiceThread(); + for (int type : deviceTypes) { + HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type); + if (device == null) { + continue; } + // TODO: Consider restoring the local device addresses from persistent storage + // to allocate the same addresses again if possible. + device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED); + mLocalDevices.put(type, device); + device.init(); } - return HdmiCec.ADDR_UNREGISTERED; } /** @@ -176,115 +164,62 @@ final class HdmiCecController { * Otherwise, scan address will start from {@code preferredAddress} * @param callback callback interface to report allocated logical address to caller */ - void allocateLogicalAddress(int deviceType, int preferredAddress, - AllocateLogicalAddressCallback callback) { - Message msg = mIoHandler.obtainMessage(MSG_ALLOCATE_LOGICAL_ADDRESS); - msg.arg1 = deviceType; - msg.arg2 = preferredAddress; - msg.obj = callback; - mIoHandler.sendMessage(msg); - } - - private static byte[] buildBody(int opcode, byte[] params) { - byte[] body = new byte[params.length + 1]; - body[0] = (byte) opcode; - System.arraycopy(params, 0, body, 1, params.length); - return body; + void allocateLogicalAddress(final int deviceType, final int preferredAddress, + final AllocateLogicalAddressCallback callback) { + assertRunOnServiceThread(); + + runOnIoThread(new Runnable() { + @Override + public void run() { + handleAllocateLogicalAddress(deviceType, preferredAddress, callback); + } + }); } - private final class IoHandler extends Handler { - private IoHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_SEND_CEC_COMMAND: - HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj; - byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams()); - nativeSendCecCommand(mNativePtr, cecMessage.getSource(), - cecMessage.getDestination(), body); - break; - case MSG_ALLOCATE_LOGICAL_ADDRESS: - int deviceType = msg.arg1; - int preferredAddress = msg.arg2; - AllocateLogicalAddressCallback callback = - (AllocateLogicalAddressCallback) msg.obj; - handleAllocateLogicalAddress(deviceType, preferredAddress, callback); - break; - default: - Slog.w(TAG, "Unsupported CEC Io request:" + msg.what); + private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress, + final AllocateLogicalAddressCallback callback) { + assertRunOnIoThread(); + int startAddress = preferredAddress; + // If preferred address is "unregistered", start address will be the smallest + // address matched with the given device type. + if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) { + for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { + if (deviceType == HdmiCec.getTypeFromAddress(i)) { + startAddress = i; break; + } } } - private void handleAllocateLogicalAddress(int deviceType, int preferredAddress, - AllocateLogicalAddressCallback callback) { - int startAddress = preferredAddress; - // If preferred address is "unregistered", start_index will be the smallest - // address matched with the given device type. - if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) { - for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { - if (deviceType == HdmiCec.getTypeFromAddress(i)) { - startAddress = i; - break; - } + int logicalAddress = HdmiCec.ADDR_UNREGISTERED; + // Iterates all possible addresses which has the same device type. + for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { + int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS; + if (curAddress != HdmiCec.ADDR_UNREGISTERED + && deviceType == HdmiCec.getTypeFromAddress(curAddress)) { + if (!sendPollMessage(curAddress, RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION)) { + logicalAddress = curAddress; + break; } } + } - int logcialAddress = HdmiCec.ADDR_UNREGISTERED; - // Iterates all possible addresses which has the same device type. - for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) { - int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS; - if (curAddress != HdmiCec.ADDR_UNREGISTERED - && deviceType == HdmiCec.getTypeFromAddress(i)) { - // <Polling Message> is a message which has empty body and - // uses same address for both source and destination address. - // If sending <Polling Message> failed (NAK), it becomes - // new logical address for the device because no device uses - // it as logical address of the device. - int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress, - EMPTY_BODY); - if (error != ERROR_SUCCESS) { - logcialAddress = curAddress; - break; - } + final int assignedAddress = logicalAddress; + if (callback != null) { + runOnServiceThread(new Runnable() { + @Override + public void run() { + callback.onAllocated(deviceType, assignedAddress); } - } - - Message msg = mControlHandler.obtainMessage(MSG_REPORT_LOGICAL_ADDRESS); - msg.arg1 = deviceType; - msg.arg2 = logcialAddress; - msg.obj = callback; - mControlHandler.sendMessage(msg); + }); } } - private final class ControlHandler extends Handler { - private ControlHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_RECEIVE_CEC_COMMAND: - // TODO: delegate it to HdmiControl service. - onReceiveCommand((HdmiCecMessage) msg.obj); - break; - case MSG_REPORT_LOGICAL_ADDRESS: - int deviceType = msg.arg1; - int logicalAddress = msg.arg2; - AllocateLogicalAddressCallback callback = - (AllocateLogicalAddressCallback) msg.obj; - callback.onAllocated(deviceType, logicalAddress); - break; - default: - Slog.i(TAG, "Unsupported message type:" + msg.what); - break; - } - } + private static byte[] buildBody(int opcode, byte[] params) { + byte[] body = new byte[params.length + 1]; + body[0] = (byte) opcode; + System.arraycopy(params, 0, body, 1, params.length); + return body; } /** @@ -298,6 +233,7 @@ final class HdmiCecController { * that has the same logical address as new one has. */ HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { + assertRunOnServiceThread(); HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress()); if (oldDeviceInfo != null) { removeDeviceInfo(deviceInfo.getLogicalAddress()); @@ -316,6 +252,7 @@ final class HdmiCecController { * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null} */ HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) { + assertRunOnServiceThread(); HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress); if (deviceInfo != null) { mDeviceInfos.remove(logicalAddress); @@ -324,13 +261,14 @@ final class HdmiCecController { } /** - * Return a list of all {@HdmiCecDeviceInfo}. + * Return a list of all {@link HdmiCecDeviceInfo}. * * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ List<HdmiCecDeviceInfo> getDeviceInfoList() { - List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<HdmiCecDeviceInfo>( - mDeviceInfos.size()); + assertRunOnServiceThread(); + + List<HdmiCecDeviceInfo> deviceInfoList = new ArrayList<>(mDeviceInfos.size()); for (int i = 0; i < mDeviceInfos.size(); ++i) { deviceInfoList.add(mDeviceInfos.valueAt(i)); } @@ -347,10 +285,22 @@ final class HdmiCecController { * Returns null if no logical address matched */ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { + assertRunOnServiceThread(); return mDeviceInfos.get(logicalAddress); } /** + * Return the locally hosted logical device of a given type. + * + * @param deviceType logical device type + * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available; + * otherwise null. + */ + HdmiCecLocalDevice getLocalDevice(int deviceType) { + return mLocalDevices.get(deviceType); + } + + /** * Add a new logical address to the device. Device's HW should be notified * when a new logical address is assigned to a device, so that it can accept * a command having available destinations. @@ -361,8 +311,8 @@ final class HdmiCecController { * @return 0 on success. Otherwise, returns negative value */ int addLogicalAddress(int newLogicalAddress) { + assertRunOnServiceThread(); if (HdmiCec.isValidAddress(newLogicalAddress)) { - mLocalAddresses.put(newLogicalAddress, newLogicalAddress); return nativeAddLogicalAddress(mNativePtr, newLogicalAddress); } else { return -1; @@ -375,9 +325,12 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ void clearLogicalAddress() { + assertRunOnServiceThread(); // TODO: consider to backup logical address so that new logical address // allocation can use it as preferred address. - mLocalAddresses.clear(); + for (int i = 0; i < mLocalDevices.size(); ++i) { + mLocalDevices.valueAt(i).clearAddress(); + } nativeClearLogicalAddress(mNativePtr); } @@ -390,6 +343,7 @@ final class HdmiCecController { * is between 0x0000 and 0xFFFF. If failed it returns -1 */ int getPhysicalAddress() { + assertRunOnServiceThread(); return nativeGetPhysicalAddress(mNativePtr); } @@ -399,6 +353,7 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ int getVersion() { + assertRunOnServiceThread(); return nativeGetVersion(mNativePtr); } @@ -408,24 +363,114 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ int getVendorId() { + assertRunOnServiceThread(); return nativeGetVendorId(mNativePtr); } - private void init(HdmiControlService service, long nativePtr) { - mService = service; - mIoHandler = new IoHandler(service.getServiceLooper()); - mControlHandler = new ControlHandler(service.getServiceLooper()); - mNativePtr = nativePtr; + /** + * Poll all remote devices. It sends <Polling Message> to all remote + * devices. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + * + * @param callback an interface used to get a list of all remote devices' address + * @param retryCount the number of retry used to send polling message to remote devices + */ + void pollDevices(DevicePollingCallback callback, int retryCount) { + assertRunOnServiceThread(); + // Extract polling candidates. No need to poll against local devices. + ArrayList<Integer> pollingCandidates = new ArrayList<>(); + for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) { + if (!isAllocatedLocalDeviceAddress(i)) { + pollingCandidates.add(i); + } + } + + runDevicePolling(pollingCandidates, retryCount, callback); + } + + private boolean isAllocatedLocalDeviceAddress(int address) { + for (int i = 0; i < mLocalDevices.size(); ++i) { + if (mLocalDevices.valueAt(i).isAddressOf(address)) { + return true; + } + } + return false; + } + + private void runDevicePolling(final List<Integer> candidates, final int retryCount, + final DevicePollingCallback callback) { + assertRunOnServiceThread(); + runOnIoThread(new Runnable() { + @Override + public void run() { + final ArrayList<Integer> allocated = new ArrayList<>(); + for (Integer address : candidates) { + if (sendPollMessage(address, retryCount)) { + allocated.add(address); + } + } + if (callback != null) { + runOnServiceThread(new Runnable() { + @Override + public void run() { + callback.onPollingFinished(allocated); + } + }); + } + } + }); + } + + private boolean sendPollMessage(int address, int retryCount) { + assertRunOnIoThread(); + for (int i = 0; i < retryCount; ++i) { + // <Polling Message> is a message which has empty body and + // uses same address for both source and destination address. + // If sending <Polling Message> failed (NAK), it becomes + // new logical address for the device because no device uses + // it as logical address of the device. + if (nativeSendCecCommand(mNativePtr, address, address, EMPTY_BODY) + == HdmiControlService.SEND_RESULT_SUCCESS) { + return true; + } + } + return false; + } + + private void assertRunOnIoThread() { + if (Looper.myLooper() != mIoHandler.getLooper()) { + throw new IllegalStateException("Should run on io thread."); + } + } + + private void assertRunOnServiceThread() { + if (Looper.myLooper() != mControlHandler.getLooper()) { + throw new IllegalStateException("Should run on service thread."); + } + } + + // Run a Runnable on IO thread. + // It should be careful to access member variables on IO thread because + // it can be accessed from system thread as well. + private void runOnIoThread(Runnable runnable) { + mIoHandler.post(runnable); + } + + private void runOnServiceThread(Runnable runnable) { + mControlHandler.post(runnable); } private boolean isAcceptableAddress(int address) { - // Can access command targeting devices available in local device or - // broadcast command. - return address == HdmiCec.ADDR_BROADCAST - || mLocalAddresses.indexOfKey(address) < 0; + // Can access command targeting devices available in local device or broadcast command. + if (address == HdmiCec.ADDR_BROADCAST) { + return true; + } + return isAllocatedLocalDeviceAddress(address); } private void onReceiveCommand(HdmiCecMessage message) { + assertRunOnServiceThread(); if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) { return; @@ -438,13 +483,34 @@ final class HdmiCecController { HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand (sourceAddress, message.getSource(), message.getOpcode(), HdmiCecMessageBuilder.ABORT_REFUSED); - sendCommand(cecMessage); + sendCommand(cecMessage, null); + } + void sendCommand(HdmiCecMessage cecMessage) { + sendCommand(cecMessage, null); } - boolean sendCommand(HdmiCecMessage cecMessage) { - Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage); - return mIoHandler.sendMessage(message); + void sendCommand(final HdmiCecMessage cecMessage, + final HdmiControlService.SendMessageCallback callback) { + runOnIoThread(new Runnable() { + @Override + public void run() { + byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams()); + final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(), + cecMessage.getDestination(), body); + if (error != HdmiControlService.SEND_RESULT_SUCCESS) { + Slog.w(TAG, "Failed to send " + cecMessage); + } + if (callback != null) { + runOnServiceThread(new Runnable() { + @Override + public void run() { + callback.onSendCompleted(error); + } + }); + } + } + }); } /** @@ -453,12 +519,15 @@ final class HdmiCecController { private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) { byte opcode = body[0]; byte params[] = Arrays.copyOfRange(body, 1, body.length); - HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params); + final HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params); // Delegate message to main handler so that it handles in main thread. - Message message = mControlHandler.obtainMessage( - MSG_RECEIVE_CEC_COMMAND, cecMessage); - mControlHandler.sendMessage(message); + runOnServiceThread(new Runnable() { + @Override + public void run() { + onReceiveCommand(cecMessage); + } + }); } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java new file mode 100644 index 0000000..e65e5fa --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -0,0 +1,103 @@ +/* + * 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 com.android.server.hdmi.HdmiCecController.AllocateLogicalAddressCallback; + +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; + +/** + * Class that models a logical CEC device hosted in this system. Handles initialization, + * CEC commands that call for actions customized per device type. + */ +abstract class HdmiCecLocalDevice { + + protected final HdmiCecController mController; + protected final int mDeviceType; + protected int mAddress; + protected int mPreferredAddress; + protected HdmiCecDeviceInfo mDeviceInfo; + + protected HdmiCecLocalDevice(HdmiCecController controller, int deviceType) { + mController = controller; + mDeviceType = deviceType; + mAddress = HdmiCec.ADDR_UNREGISTERED; + } + + // Factory method that returns HdmiCecLocalDevice of corresponding type. + static HdmiCecLocalDevice create(HdmiCecController controller, int deviceType) { + switch (deviceType) { + case HdmiCec.DEVICE_TV: + return new HdmiCecLocalDeviceTv(controller); + case HdmiCec.DEVICE_PLAYBACK: + return new HdmiCecLocalDevicePlayback(controller); + default: + return null; + } + } + + abstract void init(); + + protected void allocateAddress(int type) { + mController.allocateLogicalAddress(type, mPreferredAddress, + new AllocateLogicalAddressCallback() { + @Override + public void onAllocated(int deviceType, int logicalAddress) { + mAddress = mPreferredAddress = logicalAddress; + + // Create and set device info. + HdmiCecDeviceInfo deviceInfo = createDeviceInfo(mAddress, deviceType); + setDeviceInfo(deviceInfo); + mController.addDeviceInfo(deviceInfo); + + mController.addLogicalAddress(logicalAddress); + } + }); + } + + private final HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) { + int vendorId = mController.getVendorId(); + int physicalAddress = mController.getPhysicalAddress(); + // TODO: get device name read from system configuration. + String displayName = HdmiCec.getDefaultDeviceName(logicalAddress); + return new HdmiCecDeviceInfo(logicalAddress, + physicalAddress, deviceType, vendorId, displayName); + } + + HdmiCecDeviceInfo getDeviceInfo() { + return mDeviceInfo; + } + + void setDeviceInfo(HdmiCecDeviceInfo info) { + mDeviceInfo = info; + } + + // Returns true if the logical address is same as the argument. + boolean isAddressOf(int addr) { + return addr == mAddress; + } + + // Resets the logical address to unregistered(15), meaning the logical device is invalid. + void clearAddress() { + mAddress = HdmiCec.ADDR_UNREGISTERED; + } + + void setPreferredAddress(int addr) { + mPreferredAddress = addr; + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java new file mode 100644 index 0000000..a953467 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * Represent a logical device of type Playback residing in Android system. + */ +final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { + + HdmiCecLocalDevicePlayback(HdmiCecController controller) { + super(controller, HdmiCec.DEVICE_PLAYBACK); + } + + @Override + void init() { + allocateAddress(mDeviceType); + mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + mAddress, mController.getPhysicalAddress(), mDeviceType)); + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java new file mode 100644 index 0000000..01ea685 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -0,0 +1,43 @@ +/* + * 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; + +/** + * Represent a logical device of type TV residing in Android system. + */ +final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { + + HdmiCecLocalDeviceTv(HdmiCecController controller) { + super(controller, HdmiCec.DEVICE_TV); + } + + @Override + void init() { + allocateAddress(mDeviceType); + + // TODO: vendor-specific initialization here. + + mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + mAddress, mController.getPhysicalAddress(), mDeviceType)); + mController.sendCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( + mAddress, mController.getVendorId())); + + // TODO: Start routing control action, device discovery action. + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 09153b9..475852f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -18,27 +18,23 @@ package com.android.server.hdmi; import android.annotation.Nullable; import android.content.Context; -import android.hardware.hdmi.IHdmiControlCallback; -import android.hardware.hdmi.IHdmiControlService; -import android.hardware.hdmi.IHdmiHotplugEventListener; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.IHdmiControlCallback; +import android.hardware.hdmi.IHdmiControlService; +import android.hardware.hdmi.IHdmiHotplugEventListener; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Slog; -import android.util.SparseArray; - import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -54,6 +50,37 @@ public final class HdmiControlService extends SystemService { // TODO: Rename the permission to HDMI_CONTROL. private static final String PERMISSION = "android.permission.HDMI_CEC"; + static final int SEND_RESULT_SUCCESS = 0; + static final int SEND_RESULT_NAK = -1; + static final int SEND_RESULT_FAILURE = -2; + + /** + * Interface to report send result. + */ + interface SendMessageCallback { + /** + * Called when {@link HdmiControlService#sendCecCommand} is completed. + * + * @param error result of send request. + * @see {@link #SEND_RESULT_SUCCESS} + * @see {@link #SEND_RESULT_NAK} + * @see {@link #SEND_RESULT_FAILURE} + */ + void onSendCompleted(int error); + } + + /** + * Interface to get a list of available logical devices. + */ + interface DevicePollingCallback { + /** + * Called when device polling is finished. + * + * @param ackedAddress a list of logical addresses of available devices + */ + void onPollingFinished(List<Integer> ackedAddress); + } + // A thread to handle synchronous IO of CEC and MHL control service. // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms) // and sparse call it shares a thread to handle IO operations. @@ -151,6 +178,16 @@ public final class HdmiControlService extends SystemService { }); } + // See if we have an action of a given type in progress. + private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) { + for (FeatureAction action : mActions) { + if (action.getClass().equals(clazz)) { + return true; + } + } + return false; + } + /** * Remove the given {@link FeatureAction} object from the action queue. * @@ -205,10 +242,14 @@ public final class HdmiControlService extends SystemService { * Transmit a CEC command to CEC bus. * * @param command CEC command to send out - * @return {@code true} if succeeds to send command + * @param callback interface used to the result of send command */ - boolean sendCecCommand(HdmiCecMessage command) { - return mCecController.sendCommand(command); + void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) { + mCecController.sendCommand(command, callback); + } + + void sendCecCommand(HdmiCecMessage command) { + mCecController.sendCommand(command, null); } /** @@ -263,6 +304,17 @@ public final class HdmiControlService extends SystemService { // TODO: Start "RequestArcInitiationAction" if ARC port. } + /** + * Poll all remote devices. It sends <Polling Message> to all remote + * devices. + * + * @param callback an interface used to get a list of all remote devices' address + * @param retryCount the number of retry used to send polling message to remote devices + */ + void pollDevices(DevicePollingCallback callback, int retryCount) { + mCecController.pollDevices(callback, retryCount); + } + private void handleInitiateArc(HdmiCecMessage message){ // In case where <Initiate Arc> is started by <Request ARC Initiation> // need to clean up RequestArcInitiationAction. @@ -373,36 +425,95 @@ public final class HdmiControlService extends SystemService { } @Override - public void oneTouchPlay(IHdmiControlCallback callback) { + public void oneTouchPlay(final IHdmiControlCallback callback) { enforceAccessPermission(); - // TODO: Post a message for HdmiControlService#oneTouchPlay() + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiControlService.this.oneTouchPlay(callback); + } + }); } @Override - public void queryDisplayStatus(IHdmiControlCallback callback) { + public void queryDisplayStatus(final IHdmiControlCallback callback) { enforceAccessPermission(); - // TODO: Post a message for HdmiControlService#queryDisplayStatus() + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiControlService.this.queryDisplayStatus(callback); + } + }); } @Override - public void addHotplugEventListener(IHdmiHotplugEventListener listener) { + public void addHotplugEventListener(final IHdmiHotplugEventListener listener) { enforceAccessPermission(); - // TODO: Post a message for HdmiControlService#addHotplugEventListener() + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiControlService.this.addHotplugEventListener(listener); + } + }); } @Override - public void removeHotplugEventListener(IHdmiHotplugEventListener listener) { + public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) { enforceAccessPermission(); - // TODO: Post a message for HdmiControlService#removeHotplugEventListener() + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiControlService.this.removeHotplugEventListener(listener); + } + }); } } private void oneTouchPlay(IHdmiControlCallback callback) { - // TODO: Create a new action + if (hasAction(OneTouchPlayAction.class)) { + Slog.w(TAG, "oneTouchPlay already in progress"); + invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS); + return; + } + HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK); + if (source == null) { + Slog.w(TAG, "Local playback device not available"); + invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); + return; + } + // TODO: Consider the case of multiple TV sets. For now we always direct the command + // to the primary one. + OneTouchPlayAction action = OneTouchPlayAction.create(this, + source.getDeviceInfo().getLogicalAddress(), + source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback); + if (action == null) { + Slog.w(TAG, "Cannot initiate oneTouchPlay"); + invokeCallback(callback, HdmiCec.RESULT_EXCEPTION); + return; + } + addAndStartAction(action); } private void queryDisplayStatus(IHdmiControlCallback callback) { - // TODO: Create a new action + if (hasAction(DevicePowerStatusAction.class)) { + Slog.w(TAG, "queryDisplayStatus already in progress"); + invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS); + return; + } + HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK); + if (source == null) { + Slog.w(TAG, "Local playback device not available"); + invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); + return; + } + DevicePowerStatusAction action = DevicePowerStatusAction.create(this, + source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback); + if (action == null) { + Slog.w(TAG, "Cannot initiate queryDisplayStatus"); + invokeCallback(callback, HdmiCec.RESULT_EXCEPTION); + return; + } + addAndStartAction(action); } private void addHotplugEventListener(IHdmiHotplugEventListener listener) { @@ -431,4 +542,12 @@ public final class HdmiControlService extends SystemService { mHotplugEventListeners.remove(listener); } } + + private void invokeCallback(IHdmiControlCallback callback, int result) { + try { + callback.onComplete(result); + } catch (RemoteException e) { + Slog.e(TAG, "Invoking callback failed:" + e); + } + } } diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java index 0a701f9..343aff7 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java @@ -16,7 +16,7 @@ package com.android.server.hdmi; -import android.util.Slog; +import android.hardware.hdmi.HdmiCecMessage; /** * Feature action that handles ARC action initiated by TV devices. @@ -37,17 +37,22 @@ final class RequestArcInitiationAction extends RequestArcAction { @Override boolean start() { - if (sendCommand( - HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress, mAvrAddress))) { - mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE; - addTimer(mState, TIMEOUT_MS); - } else { - Slog.w(TAG, "Failed to send <Request ARC Initiation>"); - // If failed to send <Request ARC Initiation>, start "Disabled" ARC transmission - // action. - disableArcTransmission(); - finish(); - } + HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress, + mAvrAddress); + sendCommand(command, new HdmiControlService.SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + if (error == HdmiControlService.SEND_RESULT_SUCCESS) { + mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE; + addTimer(mState, TIMEOUT_MS); + } else { + // If failed to send <Request ARC Initiation>, start "Disabled" + // ARC transmission action. + disableArcTransmission(); + finish(); + } + } + }); return true; } } diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java index db1b992..d4a35f8 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java @@ -16,7 +16,7 @@ package com.android.server.hdmi; -import android.util.Slog; +import android.hardware.hdmi.HdmiCecMessage; /** * Feature action to handle <Request ARC Termination>. @@ -37,17 +37,22 @@ final class RequestArcTerminationAction extends RequestArcAction { @Override boolean start() { - if (sendCommand( - HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress))) { - mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE; - addTimer(mState, TIMEOUT_MS); - } else { - Slog.w(TAG, "Failed to send <Request ARC Initiation>"); - // If failed to send <Request ARC Termination>, start "Disabled" ARC transmission - // action. - disableArcTransmission(); - finish(); - } + HdmiCecMessage command = + HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress); + sendCommand(command, new HdmiControlService.SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + if (error == HdmiControlService.SEND_RESULT_SUCCESS) { + mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE; + addTimer(mState, TIMEOUT_MS); + } else { + // If failed to send <Request ARC Termination>, start "Disabled" ARC + // transmission action. + disableArcTransmission(); + finish(); + } + } + }); return true; } } diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java index 6bf149b..e3525d8 100644 --- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java +++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java @@ -64,26 +64,7 @@ final class SetArcTransmissionStateAction extends FeatureAction { @Override boolean start() { if (mEnabled) { - if (sendCommand( - HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress))) { - // Enable ARC status immediately after sending <Report Arc Initiated>. - // If AVR responds with <Feature Abort>, disable ARC status again. - // This is different from spec that says that turns ARC status to "Enabled" - // if <Report ARC Initiated> is acknowledged and no <Feature Abort> is received. - // But implemented this way to save the time having to wait for <Feature Abort>. - setArcStatus(true); - // If succeeds to send <Report ARC Initiated>, wait general timeout - // to check whether there is no <Feature Abort> for <Report ARC Initiated>. - mState = STATE_WAITING_TIMEOUT; - addTimer(mState, TIMEOUT_MS); - } else { - // If fails to send <Report ARC Initiated>, disable ARC and - // send <Report ARC Terminated> directly. - Slog.w(TAG, "Failed to send <Report ARC Initiated>:[source:" + mSourceAddress - + ", avr Address:" + mAvrAddress + "]"); - setArcStatus(false); - finish(); - } + sendReportArcInitiated(); } else { setArcStatus(false); finish(); @@ -91,6 +72,35 @@ final class SetArcTransmissionStateAction extends FeatureAction { return true; } + private void sendReportArcInitiated() { + HdmiCecMessage command = + HdmiCecMessageBuilder.buildReportArcInitiated(mSourceAddress, mAvrAddress); + sendCommand(command, new HdmiControlService.SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + if (error == HdmiControlService.SEND_RESULT_SUCCESS) { + // Enable ARC status immediately after sending <Report Arc Initiated>. + // If AVR responds with <Feature Abort>, disable ARC status again. + // This is different from spec that says that turns ARC status to + // "Enabled" if <Report ARC Initiated> is acknowledged and no + // <Feature Abort> is received. + // But implemented this way to save the time having to wait for + // <Feature Abort>. + setArcStatus(true); + // If succeeds to send <Report ARC Initiated>, wait general timeout + // to check whether there is no <Feature Abort> for <Report ARC Initiated>. + mState = STATE_WAITING_TIMEOUT; + addTimer(mState, TIMEOUT_MS); + } else { + // If fails to send <Report ARC Initiated>, disable ARC and + // send <Report ARC Terminated> directly. + setArcStatus(false); + finish(); + } + } + }); + } + private void setArcStatus(boolean enabled) { boolean wasEnabled = mService.setArcStatus(enabled); Slog.i(TAG, "Change arc status [old:" + wasEnabled + " ,new:" + enabled); diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index 66cc532..009943f 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -46,7 +46,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/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/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 b06b090..6d75ee5 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3374,25 +3374,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 +3429,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 +3468,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 +3480,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; } @@ -11475,33 +11477,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); - } - mSettings.writePackageRestrictionsLPr(userIdOrig); + 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(sourceUserId); } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 3ca658f..bb92611 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); @@ -1943,7 +1947,7 @@ final class Settings { } else if (tagName.equals(TAG_FORWARDING_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")) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 102b2d4..1bf40e0 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1140,53 +1140,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) { diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/task/StateChangedListener.java index db2d4ee..b1a4636 100644 --- a/services/core/java/com/android/server/task/StateChangedListener.java +++ b/services/core/java/com/android/server/task/StateChangedListener.java @@ -27,9 +27,8 @@ public interface StateChangedListener { /** * Called by the controller to notify the TaskManager that it should check on the state of a * task. - * @param taskStatus The state of the task which has changed. */ - public void onTaskStateChanged(TaskStatus taskStatus); + public void onControllerStateChanged(); /** * Called by the controller to notify the TaskManager that regardless of the state of the task, diff --git a/services/core/java/com/android/server/task/TaskCompletedListener.java b/services/core/java/com/android/server/task/TaskCompletedListener.java index 0210442..c53f5ca 100644 --- a/services/core/java/com/android/server/task/TaskCompletedListener.java +++ b/services/core/java/com/android/server/task/TaskCompletedListener.java @@ -16,6 +16,8 @@ package com.android.server.task; +import com.android.server.task.controllers.TaskStatus; + /** * Used for communication between {@link com.android.server.task.TaskServiceContext} and the * {@link com.android.server.task.TaskManagerService}. @@ -26,13 +28,5 @@ public interface TaskCompletedListener { * Callback for when a task is completed. * @param needsReschedule Whether the implementing class should reschedule this task. */ - public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule); - - /** - * Callback for when the implementing class needs to clean up the - * {@link com.android.server.task.TaskServiceContext}. The scheduler can get this callback - * several times if the TaskServiceContext got into a bad state (for e.g. the client crashed - * and it needs to clean up). - */ - public void onAllTasksCompleted(int serviceToken); + public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule); } diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java index 6d208ff..d5b70e6 100644 --- a/services/core/java/com/android/server/task/TaskManagerService.java +++ b/services/core/java/com/android/server/task/TaskManagerService.java @@ -16,15 +16,33 @@ package com.android.server.task; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import android.app.task.ITaskManager; +import android.app.task.Task; +import android.app.task.TaskManager; import android.content.Context; -import android.content.Task; +import android.content.pm.PackageManager; +import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.util.Log; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Slog; import android.util.SparseArray; +import com.android.server.task.controllers.ConnectivityController; +import com.android.server.task.controllers.IdleController; +import com.android.server.task.controllers.StateController; import com.android.server.task.controllers.TaskStatus; +import com.android.server.task.controllers.TimeController; + +import java.util.LinkedList; /** * Responsible for taking tasks representing work to be performed by a client app, and determining @@ -34,78 +52,151 @@ import com.android.server.task.controllers.TaskStatus; */ public class TaskManagerService extends com.android.server.SystemService implements StateChangedListener, TaskCompletedListener { + // TODO: Switch this off for final version. + private static final boolean DEBUG = true; + /** The number of concurrent tasks we run at one time. */ + private static final int MAX_TASK_CONTEXTS_COUNT = 3; static final String TAG = "TaskManager"; + /** + * When a task fails, it gets rescheduled according to its backoff policy. To be nice, we allow + * this amount of time from the rescheduled time by which the retry must occur. + */ + private static final long RESCHEDULE_WINDOW_SLOP_MILLIS = 5000L; /** Master list of tasks. */ private final TaskStore mTasks; - /** Check the pending queue and start any tasks. */ - static final int MSG_RUN_PENDING = 0; - /** Initiate the stop task flow. */ - static final int MSG_STOP_TASK = 1; - /** */ - static final int MSG_CHECK_TASKS = 2; + static final int MSG_TASK_EXPIRED = 0; + static final int MSG_CHECK_TASKS = 1; + + // Policy constants + /** + * Minimum # of idle tasks that must be ready in order to force the TM to schedule things + * early. + */ + private static final int MIN_IDLE_COUNT = 1; + /** + * Minimum # of connectivity tasks that must be ready in order to force the TM to schedule + * things early. + */ + private static final int MIN_CONNECTIVITY_COUNT = 2; + /** + * Minimum # of tasks (with no particular constraints) for which the TM will be happy running + * some work early. + */ + private static final int MIN_READY_TASKS_COUNT = 4; /** * Track Services that have currently active or pending tasks. The index is provided by * {@link TaskStatus#getServiceToken()} */ - private final SparseArray<TaskServiceContext> mActiveServices = - new SparseArray<TaskServiceContext>(); + private final List<TaskServiceContext> mActiveServices = new LinkedList<TaskServiceContext>(); + /** List of controllers that will notify this service of updates to tasks. */ + private List<StateController> mControllers; + /** + * Queue of pending tasks. The TaskServiceContext class will receive tasks from this list + * when ready to execute them. + */ + private final LinkedList<TaskStatus> mPendingTasks = new LinkedList<TaskStatus>(); private final TaskHandler mHandler; + private final TaskManagerStub mTaskManagerStub; - private class TaskHandler extends Handler { + /** + * Entry point from client to schedule the provided task. + * This will add the task to the + * @param task Task object containing execution parameters + * @param uId The package identifier of the application this task is for. + * @param canPersistTask Whether or not the client has the appropriate permissions for persisting + * of this task. + * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes. + */ + public int schedule(Task task, int uId, boolean canPersistTask) { + TaskStatus taskStatus = new TaskStatus(task, uId, canPersistTask); + return startTrackingTask(taskStatus) ? + TaskManager.RESULT_SUCCESS : TaskManager.RESULT_FAILURE; + } - public TaskHandler(Looper looper) { - super(looper); + public List<Task> getPendingTasks(int uid) { + ArrayList<Task> outList = new ArrayList<Task>(); + synchronized (mTasks) { + for (TaskStatus ts : mTasks.getTasks()) { + if (ts.getUid() == uid) { + outList.add(ts.getTask()); + } + } } + return outList; + } - @Override - public void handleMessage(Message message) { - switch (message.what) { - case MSG_RUN_PENDING: - - break; - case MSG_STOP_TASK: - - break; - case MSG_CHECK_TASKS: - checkTasks(); - break; + /** + * Entry point from client to cancel all tasks originating from their uid. + * This will remove the task from the master list, and cancel the task if it was staged for + * execution or being executed. + * @param uid To check against for removal of a task. + */ + public void cancelTaskForUid(int uid) { + // Remove from master list. + synchronized (mTasks) { + if (!mTasks.removeAllByUid(uid)) { + // If it's not in the master list, it's nowhere. + return; } } - - /** - * Called when we need to run through the list of all tasks and start/stop executing one or - * more of them. - */ - private void checkTasks() { - synchronized (mTasks) { - final SparseArray<TaskStatus> tasks = mTasks.getTasks(); - for (int i = 0; i < tasks.size(); i++) { - TaskStatus ts = tasks.valueAt(i); - if (ts.isReady() && ! isCurrentlyActive(ts)) { - assignTaskToServiceContext(ts); - } + // Remove from pending queue. + synchronized (mPendingTasks) { + Iterator<TaskStatus> it = mPendingTasks.iterator(); + while (it.hasNext()) { + TaskStatus ts = it.next(); + if (ts.getUid() == uid) { + it.remove(); + } + } + } + // Cancel if running. + synchronized (mActiveServices) { + for (TaskServiceContext tsc : mActiveServices) { + if (tsc.getRunningTask().getUid() == uid) { + tsc.cancelExecutingTask(); } } } } /** - * Entry point from client to schedule the provided task. - * This will add the task to the - * @param task Task object containing execution parameters - * @param userId The id of the user this task is for. - * @param uId The package identifier of the application this task is for. - * @param canPersistTask Whether or not the client has the appropriate permissions for persisting - * of this task. - * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes. + * Entry point from client to cancel the task corresponding to the taskId provided. + * This will remove the task from the master list, and cancel the task if it was staged for + * execution or being executed. + * @param uid Uid of the calling client. + * @param taskId Id of the task, provided at schedule-time. */ - public int schedule(Task task, int userId, int uId, boolean canPersistTask) { - TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask); - return 0; + public void cancelTask(int uid, int taskId) { + synchronized (mTasks) { + if (!mTasks.remove(uid, taskId)) { + // If it's not in the master list, it's nowhere. + return; + } + } + synchronized (mPendingTasks) { + Iterator<TaskStatus> it = mPendingTasks.iterator(); + while (it.hasNext()) { + TaskStatus ts = it.next(); + if (ts.getUid() == uid && ts.getTaskId() == taskId) { + it.remove(); + // If we got it from pending, it didn't make it to active so return. + return; + } + } + } + synchronized (mActiveServices) { + for (TaskServiceContext tsc : mActiveServices) { + if (tsc.getRunningTask().getUid() == uid && + tsc.getRunningTask().getTaskId() == taskId) { + tsc.cancelExecutingTask(); + return; + } + } + } } /** @@ -121,31 +212,148 @@ public class TaskManagerService extends com.android.server.SystemService super(context); mTasks = new TaskStore(context); mHandler = new TaskHandler(context.getMainLooper()); + mTaskManagerStub = new TaskManagerStub(); + // Create the "runners". + for (int i = 0; i < MAX_TASK_CONTEXTS_COUNT; i++) { + mActiveServices.add( + new TaskServiceContext(this, context.getMainLooper())); + } + + mControllers = new LinkedList<StateController>(); + mControllers.add(ConnectivityController.get(this)); + mControllers.add(TimeController.get(this)); + mControllers.add(IdleController.get(this)); + // TODO: Add BatteryStateController when implemented. } @Override public void onStart() { + publishBinderService(Context.TASK_SERVICE, mTaskManagerStub); + } + /** + * Called when we have a task status object that we need to insert in our + * {@link com.android.server.task.TaskStore}, and make sure all the relevant controllers know + * about. + */ + private boolean startTrackingTask(TaskStatus taskStatus) { + boolean added = false; + synchronized (mTasks) { + added = mTasks.add(taskStatus); + } + if (added) { + for (StateController controller : mControllers) { + controller.maybeStartTrackingTask(taskStatus); + } + } + return added; } - // StateChangedListener implementations. + /** + * Called when we want to remove a TaskStatus object that we've finished executing. Returns the + * object removed. + */ + private boolean stopTrackingTask(TaskStatus taskStatus) { + boolean removed; + synchronized (mTasks) { + // Remove from store as well as controllers. + removed = mTasks.remove(taskStatus); + } + if (removed) { + for (StateController controller : mControllers) { + controller.maybeStopTrackingTask(taskStatus); + } + } + return removed; + } + + private boolean cancelTaskOnServiceContext(TaskStatus ts) { + synchronized (mActiveServices) { + for (TaskServiceContext tsc : mActiveServices) { + if (tsc.getRunningTask() == ts) { + tsc.cancelExecutingTask(); + return true; + } + } + return false; + } + } /** - * Off-board work to our handler thread as quickly as possible, b/c this call is probably being - * made on the main thread. - * For now this takes the task and if it's ready to run it will run it. In future we might not - * provide the task, so that the StateChangedListener has to run through its list of tasks to - * see which are ready. This will further decouple the controllers from the execution logic. + * @param ts TaskStatus we are querying against. + * @return Whether or not the task represented by the status object is currently being run or + * is pending. */ - @Override - public void onTaskStateChanged(TaskStatus taskStatus) { - postCheckTasksMessage(); + private boolean isCurrentlyActive(TaskStatus ts) { + synchronized (mActiveServices) { + for (TaskServiceContext serviceContext : mActiveServices) { + if (serviceContext.getRunningTask() == ts) { + return true; + } + } + return false; + } + } + /** + * A task is rescheduled with exponential back-off if the client requests this from their + * execution logic. + * A caveat is for idle-mode tasks, for which the idle-mode constraint will usurp the + * timeliness of the reschedule. For an idle-mode task, no deadline is given. + * @param failureToReschedule Provided task status that we will reschedule. + * @return A newly instantiated TaskStatus with the same constraints as the last task except + * with adjusted timing constraints. + */ + private TaskStatus getRescheduleTaskForFailure(TaskStatus failureToReschedule) { + final long elapsedNowMillis = SystemClock.elapsedRealtime(); + final Task task = failureToReschedule.getTask(); + + final long initialBackoffMillis = task.getInitialBackoffMillis(); + final int backoffAttempt = failureToReschedule.getNumFailures() + 1; + long newEarliestRuntimeElapsed = elapsedNowMillis; + + switch (task.getBackoffPolicy()) { + case Task.BackoffPolicy.LINEAR: + newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt; + break; + default: + if (DEBUG) { + Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); + } + case Task.BackoffPolicy.EXPONENTIAL: + newEarliestRuntimeElapsed += Math.pow(initialBackoffMillis, backoffAttempt); + break; + } + long newLatestRuntimeElapsed = failureToReschedule.hasIdleConstraint() ? Long.MAX_VALUE + : newEarliestRuntimeElapsed + RESCHEDULE_WINDOW_SLOP_MILLIS; + return new TaskStatus(failureToReschedule, newEarliestRuntimeElapsed, + newLatestRuntimeElapsed, backoffAttempt); } - @Override - public void onTaskDeadlineExpired(TaskStatus taskStatus) { + /** + * Called after a periodic has executed so we can to re-add it. We take the last execution time + * of the task to be the time of completion (i.e. the time at which this function is called). + * This could be inaccurate b/c the task can run for as long as + * {@link com.android.server.task.TaskServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead + * to underscheduling at least, rather than if we had taken the last execution time to be the + * start of the execution. + * @return A new task representing the execution criteria for this instantiation of the + * recurring task. + */ + private TaskStatus getRescheduleTaskForPeriodic(TaskStatus periodicToReschedule) { + final long elapsedNow = SystemClock.elapsedRealtime(); + // Compute how much of the period is remaining. + long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0); + long newEarliestRunTimeElapsed = elapsedNow + runEarly; + long period = periodicToReschedule.getTask().getIntervalMillis(); + long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period; + if (DEBUG) { + Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + + newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); + } + return new TaskStatus(periodicToReschedule, newEarliestRunTimeElapsed, + newLatestRuntimeElapsed, 0 /* backoffAttempt */); } // TaskCompletedListener implementations. @@ -154,53 +362,268 @@ public class TaskManagerService extends com.android.server.SystemService * A task just finished executing. We fetch the * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on * whether we want to reschedule we readd it to the controllers. - * @param serviceToken key for the service context in {@link #mActiveServices}. - * @param taskId Id of the task that is complete. + * @param taskStatus Completed task. * @param needsReschedule Whether the implementing class should reschedule this task. */ @Override - public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) { - final TaskServiceContext serviceContext = mActiveServices.get(serviceToken); - if (serviceContext == null) { - Log.e(TAG, "Task completed for invalid service context; " + serviceToken); + public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule) { + if (!stopTrackingTask(taskStatus)) { + if (DEBUG) { + Slog.e(TAG, "Error removing task: could not find task to remove. Was task" + + "removed while executing?"); + } return; } + if (needsReschedule) { + TaskStatus rescheduled = getRescheduleTaskForFailure(taskStatus); + startTrackingTask(rescheduled); + } else if (taskStatus.getTask().isPeriodic()) { + TaskStatus rescheduledPeriodic = getRescheduleTaskForPeriodic(taskStatus); + startTrackingTask(rescheduledPeriodic); + } + mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); + } + + // StateChangedListener implementations. + /** + * Off-board work to our handler thread as quickly as possible, b/c this call is probably being + * made on the main thread. + * For now this takes the task and if it's ready to run it will run it. In future we might not + * provide the task, so that the StateChangedListener has to run through its list of tasks to + * see which are ready. This will further decouple the controllers from the execution logic. + */ + @Override + public void onControllerStateChanged() { + // Post a message to to run through the list of tasks and start/stop any that are eligible. + mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); } @Override - public void onAllTasksCompleted(int serviceToken) { - + public void onTaskDeadlineExpired(TaskStatus taskStatus) { + mHandler.obtainMessage(MSG_TASK_EXPIRED, taskStatus); } - private void assignTaskToServiceContext(TaskStatus ts) { - TaskServiceContext serviceContext = - mActiveServices.get(ts.getServiceToken()); - if (serviceContext == null) { - serviceContext = new TaskServiceContext(this, mHandler.getLooper(), ts); - mActiveServices.put(ts.getServiceToken(), serviceContext); + private class TaskHandler extends Handler { + + public TaskHandler(Looper looper) { + super(looper); } - serviceContext.addPendingTask(ts); - } - /** - * @param ts TaskStatus we are querying against. - * @return Whether or not the task represented by the status object is currently being run or - * is pending. - */ - private boolean isCurrentlyActive(TaskStatus ts) { - TaskServiceContext serviceContext = mActiveServices.get(ts.getServiceToken()); - if (serviceContext == null) { - return false; + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MSG_TASK_EXPIRED: + final TaskStatus expired = (TaskStatus) message.obj; // Unused for now. + queueReadyTasksForExecutionH(); + break; + case MSG_CHECK_TASKS: + // Check the list of tasks and run some of them if we feel inclined. + maybeQueueReadyTasksForExecutionH(); + break; + } + maybeRunNextPendingTaskH(); + // Don't remove TASK_EXPIRED in case one came along while processing the queue. + removeMessages(MSG_CHECK_TASKS); + } + + /** + * Run through list of tasks and execute all possible - at least one is expired so we do + * as many as we can. + */ + private void queueReadyTasksForExecutionH() { + synchronized (mTasks) { + for (TaskStatus ts : mTasks.getTasks()) { + final boolean criteriaSatisfied = ts.isReady(); + final boolean isRunning = isCurrentlyActive(ts); + if (criteriaSatisfied && !isRunning) { + synchronized (mPendingTasks) { + mPendingTasks.add(ts); + } + } else if (!criteriaSatisfied && isRunning) { + cancelTaskOnServiceContext(ts); + } + } + } + } + + /** + * The state of at least one task has changed. Here is where we could enforce various + * policies on when we want to execute tasks. + * Right now the policy is such: + * If >1 of the ready tasks is idle mode we send all of them off + * if more than 2 network connectivity tasks are ready we send them all off. + * If more than 4 tasks total are ready we send them all off. + * TODO: It would be nice to consolidate these sort of high-level policies somewhere. + */ + private void maybeQueueReadyTasksForExecutionH() { + synchronized (mTasks) { + int idleCount = 0; + int connectivityCount = 0; + List<TaskStatus> runnableTasks = new ArrayList<TaskStatus>(); + for (TaskStatus ts : mTasks.getTasks()) { + final boolean criteriaSatisfied = ts.isReady(); + final boolean isRunning = isCurrentlyActive(ts); + if (criteriaSatisfied && !isRunning) { + if (ts.hasIdleConstraint()) { + idleCount++; + } + if (ts.hasConnectivityConstraint() || ts.hasMeteredConstraint()) { + connectivityCount++; + } + runnableTasks.add(ts); + } else if (!criteriaSatisfied && isRunning) { + cancelTaskOnServiceContext(ts); + } + } + if (idleCount >= MIN_IDLE_COUNT || connectivityCount >= MIN_CONNECTIVITY_COUNT || + runnableTasks.size() >= MIN_READY_TASKS_COUNT) { + for (TaskStatus ts : runnableTasks) { + synchronized (mPendingTasks) { + mPendingTasks.add(ts); + } + } + } + } + } + + /** + * Checks the state of the pending queue against any available + * {@link com.android.server.task.TaskServiceContext} that can run a new task. + * {@link com.android.server.task.TaskServiceContext}. + */ + private void maybeRunNextPendingTaskH() { + TaskStatus nextPending; + synchronized (mPendingTasks) { + nextPending = mPendingTasks.poll(); + } + if (nextPending == null) { + return; + } + + synchronized (mActiveServices) { + for (TaskServiceContext tsc : mActiveServices) { + if (tsc.isAvailable()) { + if (tsc.executeRunnableTask(nextPending)) { + return; + } + } + } + } } - return serviceContext.hasTaskPending(ts); } /** - * Post a message to {@link #mHandler} to run through the list of tasks and start/stop any that - * are eligible. + * Binder stub trampoline implementation */ - private void postCheckTasksMessage() { - mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); + final class TaskManagerStub extends ITaskManager.Stub { + /** Cache determination of whether a given app can persist tasks + * key is uid of the calling app; value is undetermined/true/false + */ + private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); + + // Determine whether the caller is allowed to persist tasks, with a small cache + // because the lookup is expensive enough that we'd like to avoid repeating it. + // This must be called from within the calling app's binder identity! + private boolean canCallerPersistTasks() { + final boolean canPersist; + final int callingUid = Binder.getCallingUid(); + synchronized (mPersistCache) { + Boolean cached = mPersistCache.get(callingUid); + if (cached) { + canPersist = cached.booleanValue(); + } else { + // Persisting tasks is tantamount to running at boot, so we permit + // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED + // permission + int result = getContext().checkCallingPermission( + android.Manifest.permission.RECEIVE_BOOT_COMPLETED); + canPersist = (result == PackageManager.PERMISSION_GRANTED); + mPersistCache.put(callingUid, canPersist); + } + } + return canPersist; + } + + // ITaskManager implementation + @Override + public int schedule(Task task) throws RemoteException { + final boolean canPersist = canCallerPersistTasks(); + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + return TaskManagerService.this.schedule(task, uid, canPersist); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public List<Task> getAllPendingTasks() throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + return TaskManagerService.this.getPendingTasks(uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void cancelAll() throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + TaskManagerService.this.cancelTaskForUid(uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void cancel(int taskId) throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + TaskManagerService.this.cancelTask(uid, taskId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** + * "dumpsys" infrastructure + */ + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + + long identityToken = Binder.clearCallingIdentity(); + try { + TaskManagerService.this.dumpInternal(pw); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + }; + + void dumpInternal(PrintWriter pw) { + synchronized (mTasks) { + pw.print("Registered tasks:"); + if (mTasks.size() > 0) { + for (TaskStatus ts : mTasks.getTasks()) { + pw.println(); + ts.dump(pw, " "); + } + } else { + pw.println(); + pw.println("No tasks scheduled."); + } + } + pw.println(); } } diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java index b51cbb3..75e9212 100644 --- a/services/core/java/com/android/server/task/TaskServiceContext.java +++ b/services/core/java/com/android/server/task/TaskServiceContext.java @@ -24,6 +24,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -36,20 +37,18 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.task.controllers.TaskStatus; import java.util.concurrent.atomic.AtomicBoolean; /** - * Maintains information required to bind to a {@link android.app.task.TaskService}. This binding - * is reused to start concurrent tasks on the TaskService. Information here is unique - * to the service. - * Functionality provided by this class: - * - Managages wakelock for the service. - * - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks. - * - + * Handles client binding and lifecycle of a task. A task will only execute one at a time on an + * instance of this class. */ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection { + private static final boolean DEBUG = true; private static final String TAG = "TaskServiceContext"; /** Define the maximum # of tasks allowed to run on a service at once. */ private static final int defaultMaxActiveTasksPerService = @@ -66,10 +65,10 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon }; // States that a task occupies while interacting with the client. - private static final int VERB_STARTING = 0; - private static final int VERB_EXECUTING = 1; - private static final int VERB_STOPPING = 2; - private static final int VERB_PENDING = 3; + static final int VERB_BINDING = 0; + static final int VERB_STARTING = 1; + static final int VERB_EXECUTING = 2; + static final int VERB_STOPPING = 3; // Messages that result from interactions with the client service. /** System timed out waiting for a response. */ @@ -77,178 +76,173 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon /** Received a callback from client. */ private static final int MSG_CALLBACK = 1; /** Run through list and start any ready tasks.*/ - private static final int MSG_CHECK_PENDING = 2; - /** Cancel an active task. */ + private static final int MSG_SERVICE_BOUND = 2; + /** Cancel a task. */ private static final int MSG_CANCEL = 3; - /** Add a pending task. */ - private static final int MSG_ADD_PENDING = 4; - /** Client crashed, so we need to wind things down. */ - private static final int MSG_SHUTDOWN = 5; - - /** Used to identify this task service context when communicating with the TaskManager. */ - final int token; - final ComponentName component; - final int userId; - ITaskService service; + /** Shutdown the Task. Used when the client crashes and we can't die gracefully.*/ + private static final int MSG_SHUTDOWN_EXECUTION = 4; + private final Handler mCallbackHandler; - /** Tasks that haven't been sent to the client for execution yet. */ - private final SparseArray<ActiveTask> mPending; + /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */ + private final TaskCompletedListener mCompletedListener; /** Used for service binding, etc. */ private final Context mContext; - /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */ - final private TaskCompletedListener mCompletedListener; - private final PowerManager.WakeLock mWakeLock; + private PowerManager.WakeLock mWakeLock; - /** Whether this service is actively bound. */ - boolean mBound; + // Execution state. + private TaskParams mParams; + @VisibleForTesting + int mVerb; + private AtomicBoolean mCancelled = new AtomicBoolean(); - TaskServiceContext(TaskManagerService taskManager, Looper looper, TaskStatus taskStatus) { - mContext = taskManager.getContext(); - this.component = taskStatus.getServiceComponent(); - this.token = taskStatus.getServiceToken(); - this.userId = taskStatus.getUserId(); - mCallbackHandler = new TaskServiceHandler(looper); - mPending = new SparseArray<ActiveTask>(); - mCompletedListener = taskManager; - final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - TM_WAKELOCK_PREFIX + component.getPackageName()); - mWakeLock.setWorkSource(new WorkSource(taskStatus.getUid())); - mWakeLock.setReferenceCounted(false); - } + /** All the information maintained about the task currently being executed. */ + private TaskStatus mRunningTask; + /** Binder to the client service. */ + ITaskService service; - @Override - public void taskFinished(int taskId, boolean reschedule) { - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0) - .sendToTarget(); - } + private final Object mAvailableLock = new Object(); + /** Whether this context is free. */ + @GuardedBy("mAvailableLock") + private boolean mAvailable; - @Override - public void acknowledgeStopMessage(int taskId, boolean reschedule) { - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0) - .sendToTarget(); + TaskServiceContext(TaskManagerService service, Looper looper) { + this(service.getContext(), service, looper); } - @Override - public void acknowledgeStartMessage(int taskId, boolean ongoing) { - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, ongoing ? 1 : 0).sendToTarget(); + @VisibleForTesting + TaskServiceContext(Context context, TaskCompletedListener completedListener, Looper looper) { + mContext = context; + mCallbackHandler = new TaskServiceHandler(looper); + mCompletedListener = completedListener; } /** - * Queue up this task to run on the client. This will execute the task as quickly as possible. - * @param ts Status of the task to run. + * Give a task to this context for execution. Callers must first check {@link #isAvailable()} + * to make sure this is a valid context. + * @param ts The status of the task that we are going to run. + * @return True if the task was accepted and is going to run. */ - public void addPendingTask(TaskStatus ts) { - final TaskParams params = new TaskParams(ts.getTaskId(), ts.getExtras(), this); - final ActiveTask newTask = new ActiveTask(params, VERB_PENDING); - mCallbackHandler.obtainMessage(MSG_ADD_PENDING, newTask).sendToTarget(); - if (!mBound) { - Intent intent = new Intent().setComponent(component); - boolean binding = mContext.bindServiceAsUser(intent, this, - Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND, - new UserHandle(userId)); - if (!binding) { - Log.e(TAG, component.getShortClassName() + " unavailable."); - cancelPendingTask(ts); + boolean executeRunnableTask(TaskStatus ts) { + synchronized (mAvailableLock) { + if (!mAvailable) { + Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); + return false; + } + mAvailable = false; + } + + final PowerManager pm = + (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + TM_WAKELOCK_PREFIX + ts.getServiceComponent().getPackageName()); + mWakeLock.setWorkSource(new WorkSource(ts.getUid())); + mWakeLock.setReferenceCounted(false); + + mRunningTask = ts; + mParams = new TaskParams(ts.getTaskId(), ts.getExtras(), this); + + mVerb = VERB_BINDING; + final Intent intent = new Intent().setComponent(ts.getServiceComponent()); + boolean binding = mContext.bindServiceAsUser(intent, this, + Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND, + new UserHandle(ts.getUserId())); + if (!binding) { + if (DEBUG) { + Slog.d(TAG, ts.getServiceComponent().getShortClassName() + " unavailable."); } + return false; } + + return true; } - /** - * Called externally when a task that was scheduled for execution should be cancelled. - * @param ts The status of the task to cancel. - */ - public void cancelPendingTask(TaskStatus ts) { - mCallbackHandler.obtainMessage(MSG_CANCEL, ts.getTaskId(), -1 /* arg2 */) - .sendToTarget(); + /** Used externally to query the running task. Will return null if there is no task running. */ + TaskStatus getRunningTask() { + return mRunningTask; + } + + /** Called externally when a task that was scheduled for execution should be cancelled. */ + void cancelExecutingTask() { + mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget(); } /** - * MSG_TIMEOUT is sent with the {@link com.android.server.task.TaskServiceContext.ActiveTask} - * set in the {@link Message#obj} field. This makes it easier to remove timeouts for a given - * ActiveTask. - * @param op Operation that is taking place. + * @return Whether this context is available to handle incoming work. */ - private void scheduleOpTimeOut(ActiveTask op) { - mCallbackHandler.removeMessages(MSG_TIMEOUT, op); - - final long timeoutMillis = (op.verb == VERB_EXECUTING) ? - EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS; - if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) { - Slog.d(TAG, "Scheduling time out for '" + component.getShortClassName() + "' tId: " + - op.params.getTaskId() + ", in " + (timeoutMillis / 1000) + " s"); + boolean isAvailable() { + synchronized (mAvailableLock) { + return mAvailable; } - Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, op); - mCallbackHandler.sendMessageDelayed(m, timeoutMillis); } - /** - * @return true if this task is pending or active within this context. - */ - public boolean hasTaskPending(TaskStatus taskStatus) { - synchronized (mPending) { - return mPending.get(taskStatus.getTaskId()) != null; + @Override + public void taskFinished(int taskId, boolean reschedule) { + if (!verifyCallingUid()) { + return; + } + mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0) + .sendToTarget(); + } + + @Override + public void acknowledgeStopMessage(int taskId, boolean reschedule) { + if (!verifyCallingUid()) { + return; } + mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0) + .sendToTarget(); } - public boolean isBound() { - return mBound; + @Override + public void acknowledgeStartMessage(int taskId, boolean ongoing) { + if (!verifyCallingUid()) { + return; + } + mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, ongoing ? 1 : 0).sendToTarget(); } /** - * We acquire/release the wakelock on onServiceConnected/unbindService. This mirrors the work + * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work * we intend to send to the client - we stop sending work when the service is unbound so until * then we keep the wakelock. - * @param name The concrete component name of the service that has - * been connected. + * @param name The concrete component name of the service that has been connected. * @param service The IBinder of the Service's communication channel, */ @Override public void onServiceConnected(ComponentName name, IBinder service) { - mBound = true; + if (!name.equals(mRunningTask.getServiceComponent())) { + mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); + return; + } this.service = ITaskService.Stub.asInterface(service); - // Remove all timeouts. We've just connected to the client so there are no other - // MSG_TIMEOUTs at this point. + // Remove all timeouts. mCallbackHandler.removeMessages(MSG_TIMEOUT); mWakeLock.acquire(); - mCallbackHandler.obtainMessage(MSG_CHECK_PENDING).sendToTarget(); + mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget(); } /** - * When the client service crashes we can have a couple tasks executing, in various stages of - * undress. We'll cancel all of them and request that they be rescheduled. + * If the client service crashes we reschedule this task and clean up. * @param name The concrete component name of the service whose */ @Override public void onServiceDisconnected(ComponentName name) { - // Service disconnected... probably client crashed. - startShutdown(); + mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); } /** - * We don't just shutdown outright - we make sure the scheduler isn't going to send us any more - * tasks, then we do the shutdown. + * This class is reused across different clients, and passes itself in as a callback. Check + * whether the client exercising the callback is the client we expect. + * @return True if the binder calling is coming from the client we expect. */ - private void startShutdown() { - mCompletedListener.onAllTasksCompleted(token); - mCallbackHandler.obtainMessage(MSG_SHUTDOWN).sendToTarget(); - } - - /** Tracks a task across its various state changes. */ - private static class ActiveTask { - final TaskParams params; - int verb; - AtomicBoolean cancelled = new AtomicBoolean(); - - ActiveTask(TaskParams params, int verb) { - this.params = params; - this.verb = verb; - } - - @Override - public String toString() { - return params.getTaskId() + " " + VERB_STRINGS[verb]; + private boolean verifyCallingUid() { + if (mRunningTask == null || Binder.getCallingUid() != mRunningTask.getUid()) { + if (DEBUG) { + Slog.d(TAG, "Stale callback received, ignoring."); + } + return false; } + return true; } /** @@ -264,52 +258,67 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon @Override public void handleMessage(Message message) { switch (message.what) { - case MSG_ADD_PENDING: - if (message.obj != null) { - ActiveTask pendingTask = (ActiveTask) message.obj; - mPending.put(pendingTask.params.getTaskId(), pendingTask); - } - // fall through. - case MSG_CHECK_PENDING: - checkPendingTasksH(); + case MSG_SERVICE_BOUND: + handleServiceBoundH(); break; case MSG_CALLBACK: - ActiveTask receivedCallback = mPending.get(message.arg1); - removeMessages(MSG_TIMEOUT, receivedCallback); - - if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) { - Log.d(TAG, "MSG_CALLBACK of : " + receivedCallback); + if (DEBUG) { + Slog.d(TAG, "MSG_CALLBACK of : " + mRunningTask); } + removeMessages(MSG_TIMEOUT); - if (receivedCallback.verb == VERB_STARTING) { + if (mVerb == VERB_STARTING) { final boolean workOngoing = message.arg2 == 1; - handleStartedH(receivedCallback, workOngoing); - } else if (receivedCallback.verb == VERB_EXECUTING || - receivedCallback.verb == VERB_STOPPING) { + handleStartedH(workOngoing); + } else if (mVerb == VERB_EXECUTING || + mVerb == VERB_STOPPING) { final boolean reschedule = message.arg2 == 1; - handleFinishedH(receivedCallback, reschedule); + handleFinishedH(reschedule); } else { - if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) { - Log.d(TAG, "Unrecognised callback: " + receivedCallback); + if (DEBUG) { + Slog.d(TAG, "Unrecognised callback: " + mRunningTask); } } break; case MSG_CANCEL: - ActiveTask cancelled = mPending.get(message.arg1); - handleCancelH(cancelled); + handleCancelH(); break; case MSG_TIMEOUT: - // Timeout msgs have the ActiveTask ref so we can remove them easily. - handleOpTimeoutH((ActiveTask) message.obj); - break; - case MSG_SHUTDOWN: - handleShutdownH(); + handleOpTimeoutH(); break; + case MSG_SHUTDOWN_EXECUTION: + closeAndCleanupTaskH(true /* needsReschedule */); default: Log.e(TAG, "Unrecognised message: " + message); } } + /** Start the task on the service. */ + private void handleServiceBoundH() { + if (mVerb != VERB_BINDING) { + Slog.e(TAG, "Sending onStartTask for a task that isn't pending. " + + VERB_STRINGS[mVerb]); + closeAndCleanupTaskH(false /* reschedule */); + return; + } + if (mCancelled.get()) { + if (DEBUG) { + Slog.d(TAG, "Task cancelled while waiting for bind to complete. " + + mRunningTask); + } + closeAndCleanupTaskH(true /* reschedule */); + return; + } + try { + mVerb = VERB_STARTING; + scheduleOpTimeOut(); + service.startTask(mParams); + } catch (RemoteException e) { + Log.e(TAG, "Error sending onStart message to '" + + mRunningTask.getServiceComponent().getShortClassName() + "' ", e); + } + } + /** * State behaviours. * VERB_STARTING -> Successful start, change task to VERB_EXECUTING and post timeout. @@ -317,24 +326,25 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon * _EXECUTING -> Error * _STOPPING -> Error */ - private void handleStartedH(ActiveTask started, boolean workOngoing) { - switch (started.verb) { + private void handleStartedH(boolean workOngoing) { + switch (mVerb) { case VERB_STARTING: - started.verb = VERB_EXECUTING; + mVerb = VERB_EXECUTING; if (!workOngoing) { // Task is finished already so fast-forward to handleFinished. - handleFinishedH(started, false); + handleFinishedH(false); return; - } else if (started.cancelled.get()) { + } + if (mCancelled.get()) { // Cancelled *while* waiting for acknowledgeStartMessage from client. - handleCancelH(started); + handleCancelH(); return; - } else { - scheduleOpTimeOut(started); } + scheduleOpTimeOut(); break; default: - Log.e(TAG, "Handling started task but task wasn't starting! " + started); + Log.e(TAG, "Handling started task but task wasn't starting! Was " + + VERB_STRINGS[mVerb] + "."); return; } } @@ -345,155 +355,104 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon * _STARTING -> Error * _PENDING -> Error */ - private void handleFinishedH(ActiveTask executedTask, boolean reschedule) { - switch (executedTask.verb) { + private void handleFinishedH(boolean reschedule) { + switch (mVerb) { case VERB_EXECUTING: case VERB_STOPPING: - closeAndCleanupTaskH(executedTask, reschedule); + closeAndCleanupTaskH(reschedule); break; default: - Log.e(TAG, "Got an execution complete message for a task that wasn't being" + - "executed. " + executedTask); + Slog.e(TAG, "Got an execution complete message for a task that wasn't being" + + "executed. Was " + VERB_STRINGS[mVerb] + "."); } } /** * A task can be in various states when a cancel request comes in: - * VERB_PENDING -> Remove from queue. - * _STARTING -> Mark as cancelled and wait for {@link #acknowledgeStartMessage(int)}. + * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for + * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)} + * _STARTING -> Mark as cancelled and wait for + * {@link TaskServiceContext#acknowledgeStartMessage(int, boolean)} * _EXECUTING -> call {@link #sendStopMessageH}}. * _ENDING -> No point in doing anything here, so we ignore. */ - private void handleCancelH(ActiveTask cancelledTask) { - switch (cancelledTask.verb) { - case VERB_PENDING: - mPending.remove(cancelledTask.params.getTaskId()); - break; + private void handleCancelH() { + switch (mVerb) { + case VERB_BINDING: case VERB_STARTING: - cancelledTask.cancelled.set(true); + mCancelled.set(true); break; case VERB_EXECUTING: - cancelledTask.verb = VERB_STOPPING; - sendStopMessageH(cancelledTask); + sendStopMessageH(); break; case VERB_STOPPING: // Nada. break; default: - Log.e(TAG, "Cancelling a task without a valid verb: " + cancelledTask); + Slog.e(TAG, "Cancelling a task without a valid verb: " + mVerb); break; } } - /** - * This TaskServiceContext is shutting down. Remove all the tasks from the pending queue - * and reschedule them as if they had failed. - * Before posting this message, caller must invoke - * {@link com.android.server.task.TaskCompletedListener#onAllTasksCompleted(int)}. - */ - private void handleShutdownH() { - for (int i = 0; i < mPending.size(); i++) { - ActiveTask at = mPending.valueAt(i); - closeAndCleanupTaskH(at, true /* needsReschedule */); - } - mWakeLock.release(); - mContext.unbindService(TaskServiceContext.this); - service = null; - mBound = false; - } - - /** - * MSG_TIMEOUT gets processed here. - * @param timedOutTask The task that timed out. - */ - private void handleOpTimeoutH(ActiveTask timedOutTask) { + /** Process MSG_TIMEOUT here. */ + private void handleOpTimeoutH() { if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) { - Log.d(TAG, "MSG_TIMEOUT of " + component.getShortClassName() + " : " - + timedOutTask.params.getTaskId()); + Log.d(TAG, "MSG_TIMEOUT of " + + mRunningTask.getServiceComponent().getShortClassName() + " : " + + mParams.getTaskId()); } - final int taskId = timedOutTask.params.getTaskId(); - switch (timedOutTask.verb) { + final int taskId = mParams.getTaskId(); + switch (mVerb) { case VERB_STARTING: // Client unresponsive - wedged or failed to respond in time. We don't really // know what happened so let's log it and notify the TaskManager // FINISHED/NO-RETRY. Log.e(TAG, "No response from client for onStartTask '" + - component.getShortClassName() + "' tId: " + taskId); - closeAndCleanupTaskH(timedOutTask, false /* needsReschedule */); + mRunningTask.getServiceComponent().getShortClassName() + "' tId: " + + taskId); + closeAndCleanupTaskH(false /* needsReschedule */); break; case VERB_STOPPING: // At least we got somewhere, so fail but ask the TaskManager to reschedule. Log.e(TAG, "No response from client for onStopTask, '" + - component.getShortClassName() + "' tId: " + taskId); - closeAndCleanupTaskH(timedOutTask, true /* needsReschedule */); + mRunningTask.getServiceComponent().getShortClassName() + "' tId: " + + taskId); + closeAndCleanupTaskH(true /* needsReschedule */); break; case VERB_EXECUTING: // Not an error - client ran out of time. Log.i(TAG, "Client timed out while executing (no taskFinished received)." + " Reporting failure and asking for reschedule. " + - component.getShortClassName() + "' tId: " + taskId); - sendStopMessageH(timedOutTask); + mRunningTask.getServiceComponent().getShortClassName() + "' tId: " + + taskId); + sendStopMessageH(); break; default: Log.e(TAG, "Handling timeout for an unknown active task state: " - + timedOutTask); + + mRunningTask); return; } } /** - * Called on the handler thread. Checks the state of the pending queue and starts the task - * if it can. The task only starts if there is capacity on the service. - */ - private void checkPendingTasksH() { - if (!mBound) { - return; - } - for (int i = 0; i < mPending.size() && i < defaultMaxActiveTasksPerService; i++) { - ActiveTask at = mPending.valueAt(i); - if (at.verb != VERB_PENDING) { - continue; - } - sendStartMessageH(at); - } - } - - /** - * Already running, need to stop. Rund on handler. - * @param stoppingTask Task we are sending onStopMessage for. This task will be moved from - * VERB_EXECUTING -> VERB_STOPPING. + * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING -> + * VERB_STOPPING. */ - private void sendStopMessageH(ActiveTask stoppingTask) { - mCallbackHandler.removeMessages(MSG_TIMEOUT, stoppingTask); - if (stoppingTask.verb != VERB_EXECUTING) { - Log.e(TAG, "Sending onStopTask for a task that isn't started. " + stoppingTask); - // TODO: Handle error? + private void sendStopMessageH() { + mCallbackHandler.removeMessages(MSG_TIMEOUT); + if (mVerb != VERB_EXECUTING) { + Log.e(TAG, "Sending onStopTask for a task that isn't started. " + mRunningTask); + closeAndCleanupTaskH(false /* reschedule */); return; } try { - service.stopTask(stoppingTask.params); - stoppingTask.verb = VERB_STOPPING; - scheduleOpTimeOut(stoppingTask); + mVerb = VERB_STOPPING; + scheduleOpTimeOut(); + service.stopTask(mParams); } catch (RemoteException e) { Log.e(TAG, "Error sending onStopTask to client.", e); - closeAndCleanupTaskH(stoppingTask, false); - } - } - - /** Start the task on the service. */ - private void sendStartMessageH(ActiveTask pendingTask) { - if (pendingTask.verb != VERB_PENDING) { - Log.e(TAG, "Sending onStartTask for a task that isn't pending. " + pendingTask); - // TODO: Handle error? - } - try { - service.startTask(pendingTask.params); - pendingTask.verb = VERB_STARTING; - scheduleOpTimeOut(pendingTask); - } catch (RemoteException e) { - Log.e(TAG, "Error sending onStart message to '" + component.getShortClassName() - + "' ", e); + closeAndCleanupTaskH(false); } } @@ -503,13 +462,42 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon * or from acknowledging the stop message we sent. Either way, we're done tracking it and * we want to clean up internally. */ - private void closeAndCleanupTaskH(ActiveTask completedTask, boolean reschedule) { - removeMessages(MSG_TIMEOUT, completedTask); - mPending.remove(completedTask.params.getTaskId()); - if (mPending.size() == 0) { - startShutdown(); + private void closeAndCleanupTaskH(boolean reschedule) { + removeMessages(MSG_TIMEOUT); + mWakeLock.release(); + mContext.unbindService(TaskServiceContext.this); + mWakeLock = null; + + mRunningTask = null; + mParams = null; + mVerb = -1; + mCancelled.set(false); + + service = null; + + mCompletedListener.onTaskCompleted(mRunningTask, reschedule); + synchronized (mAvailableLock) { + mAvailable = true; + } + } + + /** + * Called when sending a message to the client, over whose execution we have no control. If we + * haven't received a response in a certain amount of time, we want to give up and carry on + * with life. + */ + private void scheduleOpTimeOut() { + mCallbackHandler.removeMessages(MSG_TIMEOUT); + + final long timeoutMillis = (mVerb == VERB_EXECUTING) ? + EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS; + if (DEBUG) { + Slog.d(TAG, "Scheduling time out for '" + + mRunningTask.getServiceComponent().getShortClassName() + "' tId: " + + mParams.getTaskId() + ", in " + (timeoutMillis / 1000) + " s"); } - mCompletedListener.onTaskCompleted(token, completedTask.params.getTaskId(), reschedule); + Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT); + mCallbackHandler.sendMessageDelayed(m, timeoutMillis); } } } diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java index 3bfc8a5..f72ab22 100644 --- a/services/core/java/com/android/server/task/TaskStore.java +++ b/services/core/java/com/android/server/task/TaskStore.java @@ -16,12 +16,18 @@ package com.android.server.task; +import android.app.task.Task; import android.content.Context; -import android.content.Task; +import android.util.ArraySet; +import android.util.Slog; import android.util.SparseArray; import com.android.server.task.controllers.TaskStatus; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + /** * Maintain a list of classes, and accessor methods/logic for these tasks. * This class offers the following functionality: @@ -35,53 +41,122 @@ import com.android.server.task.controllers.TaskStatus; * - This class is <strong>not</strong> thread-safe. */ public class TaskStore { - - /** - * Master list, indexed by {@link com.android.server.task.controllers.TaskStatus#hashCode()}. - */ - final SparseArray<TaskStatus> mTasks; + private static final String TAG = "TaskManagerStore"; + /** Threshold to adjust how often we want to write to the db. */ + private static final int MAX_OPS_BEFORE_WRITE = 1; + final ArraySet<TaskStatus> mTasks; final Context mContext; + private int mDirtyOperations; + TaskStore(Context context) { - mTasks = intialiseTaskMapFromDisk(); + mTasks = intialiseTasksFromDisk(); mContext = context; + mDirtyOperations = 0; + } + + /** + * Add a task to the master list, persisting it if necessary. If the TaskStatus already exists, + * it will be replaced. + * @param taskStatus Task to add. + * @return true if the operation succeeded. + */ + public boolean add(TaskStatus taskStatus) { + if (taskStatus.isPersisted()) { + if (!maybeWriteStatusToDisk()) { + return false; + } + } + mTasks.remove(taskStatus); + mTasks.add(taskStatus); + return true; + } + + public int size() { + return mTasks.size(); + } + + /** + * Remove the provided task. Will also delete the task if it was persisted. + * @return The TaskStatus that was removed, or null if an invalid token was provided. + */ + public boolean remove(TaskStatus taskStatus) { + boolean removed = mTasks.remove(taskStatus); + if (!removed) { + Slog.e(TAG, "Error removing task: " + taskStatus); + return false; + } else { + maybeWriteStatusToDisk(); + } + return true; } /** - * Add a task to the master list, persisting it if necessary. - * Will first check to see if the task already exists. If so, it will replace it. - * {@link android.content.pm.PackageManager} is queried to see if the calling package has - * permission to - * @param task Task to add. - * @return The initialised TaskStatus object if this operation was successful, null if it - * failed. + * Removes all TaskStatus objects for a given uid from the master list. Note that it is + * possible to remove a task that is pending/active. This operation will succeed, and the + * removal will take effect when the task has completed executing. + * @param uid Uid of the requesting app. + * @return True if at least one task was removed, false if nothing matching the provided uId + * was found. */ - public TaskStatus addNewTaskForUser(Task task, int userId, int uId, - boolean canPersistTask) { - TaskStatus taskStatus = TaskStatus.getForTaskAndUser(task, userId, uId); - if (canPersistTask && task.isPeriodic()) { - if (writeStatusToDisk()) { - mTasks.put(taskStatus.hashCode(), taskStatus); + public boolean removeAllByUid(int uid) { + Iterator<TaskStatus> it = mTasks.iterator(); + boolean removed = false; + while (it.hasNext()) { + TaskStatus ts = it.next(); + if (ts.getUid() == uid) { + it.remove(); + removed = true; } } - return taskStatus; + if (removed) { + maybeWriteStatusToDisk(); + } + return removed; } /** - * Remove the provided task. Will also delete the task if it was persisted. Note that this - * function does not return the validity of the operation, as we assume a delete will always - * succeed. - * @param task Task to remove. + * Remove the TaskStatus that matches the provided uId and taskId. Note that it is possible + * to remove a task that is pending/active. This operation will succeed, and the removal will + * take effect when the task has completed executing. + * @param uid Uid of the requesting app. + * @param taskId Task id, specified at schedule-time. + * @return true if a removal occurred, false if the provided parameters didn't match anything. */ - public void remove(Task task) { + public boolean remove(int uid, int taskId) { + Iterator<TaskStatus> it = mTasks.iterator(); + while (it.hasNext()) { + TaskStatus ts = it.next(); + if (ts.getUid() == uid && ts.getTaskId() == taskId) { + it.remove(); + maybeWriteStatusToDisk(); + return true; + } + } + return false; + } + /** + * @return The live array of TaskStatus objects. + */ + public Set<TaskStatus> getTasks() { + return mTasks; } /** * Every time the state changes we write all the tasks in one swathe, instead of trying to * track incremental changes. + * @return Whether the operation was successful. This will only fail for e.g. if the system is + * low on storage. If this happens, we continue as normal */ - private boolean writeStatusToDisk() { + private boolean maybeWriteStatusToDisk() { + mDirtyOperations++; + if (mDirtyOperations > MAX_OPS_BEFORE_WRITE) { + for (TaskStatus ts : mTasks) { + // + } + mDirtyOperations = 0; + } return true; } @@ -90,14 +165,7 @@ public class TaskStore { * @return */ // TODO: Implement this. - private SparseArray<TaskStatus> intialiseTaskMapFromDisk() { - return new SparseArray<TaskStatus>(); - } - - /** - * @return The live array of TaskStatus objects. - */ - public SparseArray<TaskStatus> getTasks() { - return mTasks; + private ArraySet<TaskStatus> intialiseTasksFromDisk() { + return new ArraySet<TaskStatus>(); } } diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java index 6a4e1f3..474af8f 100644 --- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java @@ -25,6 +25,7 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.UserHandle; import android.util.Log; +import android.util.Slog; import com.android.server.task.TaskManagerService; @@ -32,21 +33,33 @@ import java.util.LinkedList; import java.util.List; /** - * + * Handles changes in connectivity. + * We are only interested in metered vs. unmetered networks, and we're interested in them on a + * per-user basis. */ public class ConnectivityController extends StateController { private static final String TAG = "TaskManager.Connectivity"; + private static final boolean DEBUG = true; private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>(); private final BroadcastReceiver mConnectivityChangedReceiver = new ConnectivityChangedReceiver(); + /** Singleton. */ + private static ConnectivityController mSingleton; /** Track whether the latest active network is metered. */ private boolean mMetered; /** Track whether the latest active network is connected. */ private boolean mConnectivity; - public ConnectivityController(TaskManagerService service) { + public static synchronized ConnectivityController get(TaskManagerService taskManager) { + if (mSingleton == null) { + mSingleton = new ConnectivityController(taskManager); + } + return mSingleton; + } + + private ConnectivityController(TaskManagerService service) { super(service); // Register connectivity changed BR. IntentFilter intentFilter = new IntentFilter(); @@ -56,7 +69,7 @@ public class ConnectivityController extends StateController { } @Override - public void maybeTrackTaskState(TaskStatus taskStatus) { + public synchronized void maybeStartTrackingTask(TaskStatus taskStatus) { if (taskStatus.hasConnectivityConstraint() || taskStatus.hasMeteredConstraint()) { taskStatus.connectivityConstraintSatisfied.set(mConnectivity); taskStatus.meteredConstraintSatisfied.set(mMetered); @@ -65,7 +78,7 @@ public class ConnectivityController extends StateController { } @Override - public void removeTaskStateIfTracked(TaskStatus taskStatus) { + public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) { mTrackedTasks.remove(taskStatus); } @@ -73,16 +86,20 @@ public class ConnectivityController extends StateController { * @param userId Id of the user for whom we are updating the connectivity state. */ private void updateTrackedTasks(int userId) { + boolean changed = false; for (TaskStatus ts : mTrackedTasks) { - if (ts.userId != userId) { + if (ts.getUserId() != userId) { continue; } boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity); boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered); if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) { - mStateChangedListener.onTaskStateChanged(ts); + changed = true; } } + if (changed) { + mStateChangedListener.onControllerStateChanged(); + } } class ConnectivityChangedReceiver extends BroadcastReceiver { @@ -106,7 +123,7 @@ public class ConnectivityController extends StateController { context.getSystemService(Context.CONNECTIVITY_SERVICE); final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo(); // This broadcast gets sent a lot, only update if the active network has changed. - if (activeNetwork.getType() == networkType) { + if (activeNetwork != null && activeNetwork.getType() == networkType) { final int userid = context.getUserId(); mMetered = false; mConnectivity = @@ -117,7 +134,9 @@ public class ConnectivityController extends StateController { updateTrackedTasks(userid); } } else { - Log.w(TAG, "Unrecognised action in intent: " + action); + if (DEBUG) { + Slog.d(TAG, "Unrecognised action in intent: " + action); + } } } }; diff --git a/services/core/java/com/android/server/task/controllers/IdleController.java b/services/core/java/com/android/server/task/controllers/IdleController.java index a319a31..9489644 100644 --- a/services/core/java/com/android/server/task/controllers/IdleController.java +++ b/services/core/java/com/android/server/task/controllers/IdleController.java @@ -49,7 +49,7 @@ public class IdleController extends StateController { private static Object sCreationLock = new Object(); private static volatile IdleController sController; - public IdleController getController(TaskManagerService service) { + public static IdleController get(TaskManagerService service) { synchronized (sCreationLock) { if (sController == null) { sController = new IdleController(service); @@ -67,7 +67,7 @@ public class IdleController extends StateController { * StateController interface */ @Override - public void maybeTrackTaskState(TaskStatus taskStatus) { + public void maybeStartTrackingTask(TaskStatus taskStatus) { if (taskStatus.hasIdleConstraint()) { synchronized (mTrackedTasks) { mTrackedTasks.add(taskStatus); @@ -77,7 +77,7 @@ public class IdleController extends StateController { } @Override - public void removeTaskStateIfTracked(TaskStatus taskStatus) { + public void maybeStopTrackingTask(TaskStatus taskStatus) { synchronized (mTrackedTasks) { mTrackedTasks.remove(taskStatus); } @@ -90,9 +90,9 @@ public class IdleController extends StateController { synchronized (mTrackedTasks) { for (TaskStatus task : mTrackedTasks) { task.idleConstraintSatisfied.set(isIdle); - mStateChangedListener.onTaskStateChanged(task); } } + mStateChangedListener.onControllerStateChanged(); } /** diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/task/controllers/StateController.java index e1cd662..ed31eac 100644 --- a/services/core/java/com/android/server/task/controllers/StateController.java +++ b/services/core/java/com/android/server/task/controllers/StateController.java @@ -42,10 +42,10 @@ public abstract class StateController { * Also called when updating a task, so implementing controllers have to be aware of * preexisting tasks. */ - public abstract void maybeTrackTaskState(TaskStatus taskStatus); + public abstract void maybeStartTrackingTask(TaskStatus taskStatus); /** * Remove task - this will happen if the task is cancelled, completed, etc. */ - public abstract void removeTaskStateIfTracked(TaskStatus taskStatus); + public abstract void maybeStopTrackingTask(TaskStatus taskStatus); } diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java index d96fedc..b7f84ec 100644 --- a/services/core/java/com/android/server/task/controllers/TaskStatus.java +++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java @@ -16,17 +16,18 @@ package com.android.server.task.controllers; +import android.app.task.Task; import android.content.ComponentName; -import android.content.Task; -import android.content.pm.PackageParser; import android.os.Bundle; import android.os.SystemClock; +import android.os.UserHandle; +import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicBoolean; /** * Uniquely identifies a task internally. - * Created from the public {@link android.content.Task} object when it lands on the scheduler. + * Created from the public {@link android.app.task.Task} object when it lands on the scheduler. * Contains current state of the requirements of the task, as well as a function to evaluate * whether it's ready to run. * This object is shared among the various controllers - hence why the different fields are atomic. @@ -36,80 +37,88 @@ import java.util.concurrent.atomic.AtomicBoolean; * @hide */ public class TaskStatus { - final int taskId; - final int userId; + final Task task; final int uId; - final ComponentName component; - final Bundle extras; + /** At reschedule time we need to know whether to update task on disk. */ + final boolean persisted; + + // Constraints. final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean(); - final AtomicBoolean timeConstraintSatisfied = new AtomicBoolean(); + final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean(); + final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean(); final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean(); final AtomicBoolean meteredConstraintSatisfied = new AtomicBoolean(); final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean(); - private final boolean hasChargingConstraint; - private final boolean hasTimingConstraint; - private final boolean hasIdleConstraint; - private final boolean hasMeteredConstraint; - private final boolean hasConnectivityConstraint; - + /** + * Earliest point in the future at which this task will be eligible to run. A value of 0 + * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}. + */ private long earliestRunTimeElapsedMillis; + /** + * Latest point in the future at which this task must be run. A value of {@link Long#MAX_VALUE} + * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}. + */ private long latestRunTimeElapsedMillis; - /** Provide a unique handle to the service that this task will be run on. */ - public int getServiceToken() { - return component.hashCode() + userId; - } + private final int numFailures; - /** Generate a TaskStatus object for a given task and uid. */ - // TODO: reimplement this to reuse these objects instead of creating a new one each time? - public static TaskStatus getForTaskAndUser(Task task, int userId, int uId) { - return new TaskStatus(task, userId, uId); + /** Provide a handle to the service that this task will be run on. */ + public int getServiceToken() { + return uId; } - /** Set up the state of a newly scheduled task. */ - TaskStatus(Task task, int userId, int uId) { - this.taskId = task.getTaskId(); - this.userId = userId; - this.component = task.getService(); - this.extras = task.getExtras(); + /** Create a newly scheduled task. */ + public TaskStatus(Task task, int uId, boolean persisted) { + this.task = task; this.uId = uId; + this.numFailures = 0; + this.persisted = persisted; - hasChargingConstraint = task.isRequireCharging(); - hasIdleConstraint = task.isRequireDeviceIdle(); - + final long elapsedNow = SystemClock.elapsedRealtime(); // Timing constraints if (task.isPeriodic()) { - long elapsedNow = SystemClock.elapsedRealtime(); earliestRunTimeElapsedMillis = elapsedNow; latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis(); - hasTimingConstraint = true; - } else if (task.getMinLatencyMillis() != 0L || task.getMaxExecutionDelayMillis() != 0L) { - earliestRunTimeElapsedMillis = task.getMinLatencyMillis() > 0L ? - task.getMinLatencyMillis() : Long.MAX_VALUE; - latestRunTimeElapsedMillis = task.getMaxExecutionDelayMillis() > 0L ? - task.getMaxExecutionDelayMillis() : Long.MAX_VALUE; - hasTimingConstraint = true; } else { - hasTimingConstraint = false; + earliestRunTimeElapsedMillis = task.hasEarlyConstraint() ? + elapsedNow + task.getMinLatencyMillis() : 0L; + latestRunTimeElapsedMillis = task.hasLateConstraint() ? + elapsedNow + task.getMaxExecutionDelayMillis() : Long.MAX_VALUE; } + } - // Networking constraints - hasMeteredConstraint = task.getNetworkCapabilities() == Task.NetworkType.UNMETERED; - hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY; + public TaskStatus(TaskStatus rescheduling, long newEarliestRuntimeElapsed, + long newLatestRuntimeElapsed, int backoffAttempt) { + this.task = rescheduling.task; + + this.uId = rescheduling.getUid(); + this.persisted = rescheduling.isPersisted(); + this.numFailures = backoffAttempt; + + earliestRunTimeElapsedMillis = newEarliestRuntimeElapsed; + latestRunTimeElapsedMillis = newLatestRuntimeElapsed; + } + + public Task getTask() { + return task; } public int getTaskId() { - return taskId; + return task.getId(); + } + + public int getNumFailures() { + return numFailures; } public ComponentName getServiceComponent() { - return component; + return task.getService(); } public int getUserId() { - return userId; + return UserHandle.getUserId(uId); } public int getUid() { @@ -117,53 +126,61 @@ public class TaskStatus { } public Bundle getExtras() { - return extras; + return task.getExtras(); } - boolean hasConnectivityConstraint() { - return hasConnectivityConstraint; + public boolean hasConnectivityConstraint() { + return task.getNetworkCapabilities() == Task.NetworkType.ANY; } - boolean hasMeteredConstraint() { - return hasMeteredConstraint; + public boolean hasMeteredConstraint() { + return task.getNetworkCapabilities() == Task.NetworkType.UNMETERED; } - boolean hasChargingConstraint() { - return hasChargingConstraint; + public boolean hasChargingConstraint() { + return task.isRequireCharging(); } - boolean hasTimingConstraint() { - return hasTimingConstraint; + public boolean hasTimingDelayConstraint() { + return earliestRunTimeElapsedMillis != 0L; } - boolean hasIdleConstraint() { - return hasIdleConstraint; + public boolean hasDeadlineConstraint() { + return latestRunTimeElapsedMillis != Long.MAX_VALUE; } - long getEarliestRunTime() { + public boolean hasIdleConstraint() { + return task.isRequireDeviceIdle(); + } + + public long getEarliestRunTime() { return earliestRunTimeElapsedMillis; } - long getLatestRunTime() { + public long getLatestRunTimeElapsed() { return latestRunTimeElapsedMillis; } + public boolean isPersisted() { + return persisted; + } /** - * @return whether this task is ready to run, based on its requirements. + * @return Whether or not this task is ready to run, based on its requirements. */ public synchronized boolean isReady() { - return (!hasChargingConstraint || chargingConstraintSatisfied.get()) - && (!hasTimingConstraint || timeConstraintSatisfied.get()) - && (!hasConnectivityConstraint || connectivityConstraintSatisfied.get()) - && (!hasMeteredConstraint || meteredConstraintSatisfied.get()) - && (!hasIdleConstraint || idleConstraintSatisfied.get()); + return (!hasChargingConstraint() || chargingConstraintSatisfied.get()) + && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get()) + && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get()) + && (!hasMeteredConstraint() || meteredConstraintSatisfied.get()) + && (!hasIdleConstraint() || idleConstraintSatisfied.get()) + && (!hasDeadlineConstraint() || deadlineConstraintSatisfied.get()); } @Override public int hashCode() { - int result = component.hashCode(); - result = 31 * result + taskId; - result = 31 * result + userId; + int result = getServiceComponent().hashCode(); + result = 31 * result + task.getId(); + result = 31 * result + uId; return result; } @@ -173,8 +190,15 @@ public class TaskStatus { if (!(o instanceof TaskStatus)) return false; TaskStatus that = (TaskStatus) o; - return ((taskId == that.taskId) - && (userId == that.userId) - && (component.equals(that.component))); + return ((task.getId() == that.task.getId()) + && (uId == that.uId) + && (getServiceComponent().equals(that.getServiceComponent()))); + } + + // Dumpsys infrastructure + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); pw.print("Task "); pw.println(task.getId()); + pw.print(prefix); pw.print("uid="); pw.println(uId); + pw.print(prefix); pw.print("component="); pw.println(task.getService()); } } diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/task/controllers/TimeController.java index 6d97a53..72f312c 100644 --- a/services/core/java/com/android/server/task/controllers/TimeController.java +++ b/services/core/java/com/android/server/task/controllers/TimeController.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.SystemClock; -import android.util.Log; import com.android.server.task.TaskManagerService; @@ -54,8 +53,17 @@ public class TimeController extends StateController { private AlarmManager mAlarmService = null; /** List of tracked tasks, sorted asc. by deadline */ private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>(); + /** Singleton. */ + private static TimeController mSingleton; - public TimeController(TaskManagerService service) { + public static synchronized TimeController get(TaskManagerService taskManager) { + if (mSingleton == null) { + mSingleton = new TimeController(taskManager); + } + return mSingleton; + } + + private TimeController(TaskManagerService service) { super(service); mTaskExpiredAlarmIntent = PendingIntent.getBroadcast(mContext, 0 /* ignored */, @@ -75,8 +83,8 @@ public class TimeController extends StateController { * list. */ @Override - public synchronized void maybeTrackTaskState(TaskStatus task) { - if (task.hasTimingConstraint()) { + public synchronized void maybeStartTrackingTask(TaskStatus task) { + if (task.hasTimingDelayConstraint()) { ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size()); while (it.hasPrevious()) { TaskStatus ts = it.previous(); @@ -85,13 +93,13 @@ public class TimeController extends StateController { it.remove(); it.add(task); break; - } else if (ts.getLatestRunTime() < task.getLatestRunTime()) { + } else if (ts.getLatestRunTimeElapsed() < task.getLatestRunTimeElapsed()) { // Insert it.add(task); break; } } - maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTime()); + maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTimeElapsed()); } } @@ -100,12 +108,12 @@ public class TimeController extends StateController { * so, update them. */ @Override - public synchronized void removeTaskStateIfTracked(TaskStatus taskStatus) { + public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) { if (mTrackedTasks.remove(taskStatus)) { if (mNextDelayExpiredElapsedMillis <= taskStatus.getEarliestRunTime()) { handleTaskDelayExpired(); } - if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTime()) { + if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTimeElapsed()) { handleTaskDeadlineExpired(); } } @@ -140,10 +148,10 @@ public class TimeController extends StateController { * back and forth. */ private boolean canStopTrackingTask(TaskStatus taskStatus) { - final long elapsedNowMillis = SystemClock.elapsedRealtime(); - return taskStatus.timeConstraintSatisfied.get() && - (taskStatus.getLatestRunTime() == Long.MAX_VALUE || - taskStatus.getLatestRunTime() < elapsedNowMillis); + return (!taskStatus.hasTimingDelayConstraint() || + taskStatus.timeDelayConstraintSatisfied.get()) && + (!taskStatus.hasDeadlineConstraint() || + taskStatus.deadlineConstraintSatisfied.get()); } private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) { @@ -174,10 +182,10 @@ public class TimeController extends StateController { Iterator<TaskStatus> it = mTrackedTasks.iterator(); while (it.hasNext()) { TaskStatus ts = it.next(); - final long taskDeadline = ts.getLatestRunTime(); + final long taskDeadline = ts.getLatestRunTimeElapsed(); if (taskDeadline <= nowElapsedMillis) { - ts.timeConstraintSatisfied.set(true); + ts.deadlineConstraintSatisfied.set(true); mStateChangedListener.onTaskDeadlineExpired(ts); it.remove(); } else { // Sorted by expiry time, so take the next one and stop. @@ -199,10 +207,12 @@ public class TimeController extends StateController { Iterator<TaskStatus> it = mTrackedTasks.iterator(); while (it.hasNext()) { final TaskStatus ts = it.next(); + if (!ts.hasTimingDelayConstraint()) { + continue; + } final long taskDelayTime = ts.getEarliestRunTime(); if (taskDelayTime < nowElapsedMillis) { - ts.timeConstraintSatisfied.set(true); - mStateChangedListener.onTaskStateChanged(ts); + ts.timeDelayConstraintSatisfied.set(true); if (canStopTrackingTask(ts)) { it.remove(); } @@ -212,6 +222,7 @@ public class TimeController extends StateController { } } } + mStateChangedListener.onControllerStateChanged(); maybeUpdateAlarms(nextDelayTime, Long.MAX_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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5395f60..d78fb13 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3303,7 +3303,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void addForwardingIntentFilter(ComponentName who, IntentFilter filter, int flags) { + public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) { int callingUserId = UserHandle.getCallingUserId(); synchronized (this) { if (who == null) { @@ -3314,12 +3314,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 +3330,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 +3340,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 de46b16..bc34e0e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -83,6 +83,7 @@ import com.android.server.power.ShutdownThread; import com.android.server.search.SearchManagerService; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.storage.DeviceStorageMonitorService; +import com.android.server.task.TaskManagerService; import com.android.server.trust.TrustManagerService; import com.android.server.tv.TvInputManagerService; import com.android.server.twilight.TwilightService; @@ -132,6 +133,8 @@ public final class SystemServer { "com.android.server.hdmi.HdmiCecService"; private static final String ETHERNET_SERVICE_CLASS = "com.android.server.ethernet.EthernetService"; + private static final String TASK_SERVICE_CLASS = + "com.android.server.task.TaskManagerService"; private final int mFactoryTestMode; private Timer mProfilerSnapshotTimer; @@ -831,6 +834,8 @@ public final class SystemServer { mSystemServiceManager.startService(UiModeManagerService.class); + mSystemServiceManager.startService(TaskManagerService.class); + if (!disableNonCoreServices) { try { if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) { diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl new file mode 100644 index 0000000..c439211 --- /dev/null +++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl @@ -0,0 +1,34 @@ +/* + * 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.telecomm; + +/** + * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing + * commands that were previously handled by ITelephony. + * {@hide} + */ +oneway interface ITelecommService { + + /** + * Silence the ringer if an incoming call is currently ringing. + * (If vibrating, stop the vibrator also.) + * + * It's safe to call this if the ringer has already been silenced, or + * even if there's no incoming call. (If so, this method will do nothing.) + */ + void silenceRinger(); +} diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a896861..2483419 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -30,6 +30,7 @@ import android.os.SystemProperties; import android.telephony.Rlog; import android.util.Log; +import com.android.internal.telecomm.ITelecommService; import com.android.internal.telephony.IPhoneSubInfo; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.ITelephonyRegistry; @@ -65,6 +66,8 @@ import java.util.regex.Pattern; public class TelephonyManager { private static final String TAG = "TelephonyManager"; + private static final String TELECOMM_SERVICE_NAME = "telecomm"; + private static ITelephonyRegistry sRegistry; /** @@ -1536,6 +1539,10 @@ public class TelephonyManager { return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); } + private ITelecommService getTelecommService() { + return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME)); + } + // // // PhoneStateListener @@ -1745,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 { @@ -1766,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 { @@ -1795,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) { @@ -1819,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 { @@ -2016,9 +2031,9 @@ public class TelephonyManager { @PrivateApi public void silenceRinger() { try { - getITelephony().silenceRinger(); + getTelecommService().silenceRinger(); } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#silenceRinger", e); + Log.e(TAG, "Error calling ITelecommService#silenceRinger", e); } } 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/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml index 118f258..d0f2a2d 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml @@ -24,12 +24,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..db5697c 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,21 @@ <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" /> + <viewport android:viewportWidth="320" + android:viewportHeight="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" - android:rotation="180" - android:stroke="#FF00FF00" - android:strokeWidth="10" - android:trimPathEnd=".9" - android:trimPathStart=".1" /> - -</vector>
\ No newline at end of file + 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:rotation="180" + android:pivotX="70" + android:pivotY="120" + android:trimPathStart=".1" + android:trimPathEnd=".9"/> +</vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml index 451b28e..8e8250d9 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.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,54 +15,57 @@ <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:pathData=" - M 0, 0 - l 7.3, 0 - l 0, 0 - l -7.3, 0 - z" - android:pivotX="3.65" - android:pivotY="6.125" - android:rotation="-30" /> + android:name="clip1" + android:pathData=" + M 0, 0 + l 7.3, 0 + l 0, 0 + l -7.3, 0 + z" + android:clipToPath="true" + android:rotation="-30" + android:pivotX="3.65" + android:pivotY="6.125" + /> <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" /> + 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" + android:fill="#ff88ff" + /> <path - android:name="clip2" - android:clipToPath="true" - android:pathData=" - M 0, 0 - l 7.3, 0 - l 0, 12.25 - l -7.3, 0 - z" - android:pivotX="3.65" - android:pivotY="6.125" - android:rotation="-30" /> + android:name="clip2" + android:pathData=" + M 0, 0 + l 7.3, 0 + l 0, 12.25 + l -7.3, 0 + z" + android:clipToPath="true" + android:rotation="-30" + android:pivotX="3.65" + android:pivotY="6.125" + /> <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 - q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875 - 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 + 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 + q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875 + 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" + android:fill="#ff88ff" + /> +</vector> 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..2e379d6 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" - android:pivotX="100" - android:pivotY="100" - android:rotation="90" /> + <group> + <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" + android:pivotX="100" + android:pivotY="100" + android:rotation="90" /> + </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..2b6c5d3 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml @@ -23,16 +23,18 @@ 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: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> </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..681eb4f 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml @@ -23,20 +23,22 @@ 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" + android:pivotX="300" + android:pivotY="300" + android:rotation="0" /> + </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..ef1b8e4 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml @@ -23,20 +23,22 @@ 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:pivotX="300" + android:pivotY="200" + android:rotation="0" + 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..77bf723 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> + <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:pivotX="90" + android:pivotY="100" + android:rotation="20" + 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..df5838c 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" - android:pivotX="250" - android:pivotY="200" - android:rotation="180" - android:stroke="#FFFF0000" - android:strokeWidth="10" /> + <group> + <path + android:name="house" + android:fill="#ff440000" + android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" + android:pivotX="250" + android:pivotY="200" + android:rotation="180" + 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..0bdcda5 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml @@ -23,13 +23,15 @@ android:viewportHeight="200" android:viewportWidth="200" /> - <path - android:name="house" - android:pathData="M 100,10 v 90 M 10,100 h 90" - android:pivotX="100" - android:pivotY="100" - android:rotation="360" - android:stroke="#FF00FF00" - android:strokeWidth="10" /> + <group> + <path + android:name="house" + android:pathData="M 100,10 v 90 M 10,100 h 90" + android:pivotX="100" + android:pivotY="100" + android:rotation="360" + 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..4453ae4 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,21 @@ <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" + android:rotation="360" + android:pivotX="600" + android:pivotY="300"/> + </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..dfae9ac 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml @@ -23,13 +23,15 @@ 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:pivotX="250" + android:pivotY="200" + android:rotation="360" + 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_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/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java index b918cdd..dcc7769 100644 --- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java @@ -68,7 +68,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 +80,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/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java index 31d1594..f4a9f52 100644 --- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java @@ -97,6 +97,13 @@ public class Resources_Theme_Delegate { return found; } + @LayoutlibDelegate + /*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme, + int[] values, int[] attrs) { + // FIXME + return null; + } + // ---- private helper methods ---- private static boolean setupResources(Theme thisTheme) { diff --git a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java index 0a7899a..5d89f83 100644 --- a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java @@ -27,4 +27,10 @@ public class TypedArray_Delegate { // pass return false; } + + @LayoutlibDelegate + /*package*/ static TypedArray obtain(Resources res, int len) { + // FIXME + return null; + } } diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java index 802cf1c..33813d1 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java +++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java @@ -18,9 +18,11 @@ package android.graphics; import java.awt.Font; import java.awt.Graphics2D; +import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.Rectangle2D; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -36,12 +38,12 @@ import android.graphics.Paint_Delegate.FontInfo; @SuppressWarnings("deprecation") public class BidiRenderer { - /* package */ static class ScriptRun { + /*package*/ static class ScriptRun { int start; int limit; boolean isRtl; int scriptCode; - FontInfo font; + Font font; public ScriptRun(int start, int limit, boolean isRtl) { this.start = start; @@ -51,9 +53,10 @@ public class BidiRenderer { } } - private Graphics2D mGraphics; - private Paint_Delegate mPaint; + private final Graphics2D mGraphics; + private final Paint_Delegate mPaint; private char[] mText; + private List<Font> mFonts; // Bounds of the text drawn so far. private RectF mBounds; private float mBaseline; @@ -63,11 +66,15 @@ public class BidiRenderer { * @param paint The Paint to use to get the fonts. Should not be null. * @param text Unidirectional text. Should not be null. */ - /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) { + /*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) { assert (paint != null); mGraphics = graphics; mPaint = paint; mText = text; + mFonts = new ArrayList<Font>(paint.getFonts().size()); + for (FontInfo fontInfo : paint.getFonts()) { + mFonts.add(fontInfo.mFont); + } } /** @@ -94,7 +101,7 @@ public class BidiRenderer { // the script runs. mBounds = new RectF(x, y, x, y); mBaseline = y; - for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) { + for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) { int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT; flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT; renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw); @@ -108,16 +115,15 @@ public class BidiRenderer { * much as possible. This also implements a fallback mechanism to render characters that cannot * be drawn using the preferred font. */ - private void renderScript(int start, int limit, FontInfo preferredFont, int flag, + private void renderScript(int start, int limit, Font preferredFont, int flag, float[] advances, int advancesIndex, boolean draw) { - List<FontInfo> fonts = mPaint.getFonts(); - if (fonts == null || preferredFont == null) { + if (mFonts.size() == 0 || preferredFont == null) { return; } while (start < limit) { boolean foundFont = false; - int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, start, limit); + int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit); if (canDisplayUpTo == -1) { // We can draw all characters in the text. render(start, limit, preferredFont, flag, advances, advancesIndex, draw); @@ -133,8 +139,8 @@ public class BidiRenderer { // The current character cannot be drawn with the preferred font. Cycle through all the // fonts to check which one can draw it. int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1; - for (FontInfo font : fonts) { - canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount); + for (Font font : mFonts) { + canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount); if (canDisplayUpTo == -1) { render(start, start+charCount, font, flag, advances, advancesIndex, draw); start += charCount; @@ -160,15 +166,19 @@ public class BidiRenderer { * Renders the text to the right of the bounds with the given font. * @param font The font to render the text with. */ - private void render(int start, int limit, FontInfo font, int flag, float[] advances, + private void render(int start, int limit, Font font, int flag, float[] advances, int advancesIndex, boolean draw) { - // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with - // the anti-aliasing set. - FontRenderContext f = font.mMetrics.getFontRenderContext(); - FontRenderContext frc = new FontRenderContext(f.getTransform(), mPaint.isAntiAliased(), - f.usesFractionalMetrics()); - GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag); + FontRenderContext frc; + if (mGraphics != null) { + frc = mGraphics.getFontRenderContext(); + } else { + frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext(); + // Metrics obtained this way don't have anti-aliasing set. So, + // we create a new FontRenderContext with anti-aliasing set. + frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics()); + } + GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag); int ng = gv.getNumGlyphs(); int[] ci = gv.getGlyphCharIndices(0, ng, null); if (advances != null) { @@ -206,7 +216,7 @@ public class BidiRenderer { } /* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit, - boolean isRtl, List<FontInfo> fonts) { + boolean isRtl, List<Font> fonts) { LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>(); int count = limit - start; @@ -225,10 +235,10 @@ public class BidiRenderer { // TODO: Replace this method with one which returns the font based on the scriptCode. private static void setScriptFont(char[] text, ScriptRun run, - List<FontInfo> fonts) { - for (FontInfo fontInfo : fonts) { - if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) { - run.font = fontInfo; + List<Font> fonts) { + for (Font font : fonts) { + if (font.canDisplayUpTo(text, run.start, run.limit) == -1) { + run.font = font; return; } } diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 56c0de9..e9daffd 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -979,7 +979,6 @@ public final class Canvas_Delegate { final float startX, final float startY, final int flags, long paint, long typeface) { - // TODO: use typeface. draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override @@ -1097,7 +1096,7 @@ public final class Canvas_Delegate { /** * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint. * <p>Note that the drawable may actually be executed several times if there are - * layers involved (see {@link #saveLayer(RectF, int, int)}. + * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}. */ private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode, GcSnapshot.Drawable drawable) { @@ -1117,7 +1116,7 @@ public final class Canvas_Delegate { * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}. * <p>Note that the drawable may actually be executed several times if there are - * layers involved (see {@link #saveLayer(RectF, int, int)}. + * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}. */ private static void draw(long nCanvas, GcSnapshot.Drawable drawable) { // get the delegate from the native int. @@ -1190,12 +1189,6 @@ public final class Canvas_Delegate { return mSnapshot.clipRect(left, top, right, bottom, regionOp); } - private void setBitmap(Bitmap_Delegate bitmap) { - mBitmap = bitmap; - assert mSnapshot.size() == 1; - mSnapshot.setBitmap(mBitmap); - } - private static void drawBitmap( long nativeCanvas, Bitmap_Delegate bitmap, diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java new file mode 100644 index 0000000..5e7543a --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java @@ -0,0 +1,225 @@ +/* + * 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.graphics; + +import com.android.ide.common.rendering.api.LayoutLog; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import java.awt.Font; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import static android.graphics.Typeface_Delegate.SYSTEM_FONTS; + +/** + * Delegate implementing the native methods of android.graphics.FontFamily + * + * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced + * by calls to methods of the same name in this delegate class. + * + * This class behaves like the original native implementation, but in Java, keeping previously + * native data into its own objects and mapping them to int that are sent back and forth between + * it and the original FontFamily class. + * + * @see DelegateManager + */ +public class FontFamily_Delegate { + + // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked + // separately. + private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf"; + private static final String FONT_SUFFIX_BOLD = "Bold.ttf"; + private static final String FONT_SUFFIX_ITALIC = "Italic.ttf"; + private static final String FONT_SUBSTRING_COMPACT = "UI"; + + /** + * A class associating {@link Font} with its metadata. + */ + private static final class FontInfo { + Font mFont; + /** Regular, Bold, Italic, or BoldItalic. */ + int mStyle; + /** + * The variant of the Font - compact or elegant. + * @see Paint#setElegantTextHeight(boolean) + */ + boolean mIsCompact; + } + + // ---- delegate manager ---- + private static final DelegateManager<FontFamily_Delegate> sManager = + new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class); + + // ---- delegate helper data ---- + private static String sFontLocation; + private static final List<FontFamily_Delegate> sPostInitDelegate = new + ArrayList<FontFamily_Delegate>(); + + + // ---- delegate data ---- + private List<FontInfo> mFonts = new ArrayList<FontInfo>(); + // Path of fonts that haven't been created since sFontLoader hasn't been initialized. + private List<String> mPath = new ArrayList<String>(); + + + // ---- Public Helper methods ---- + + public static FontFamily_Delegate getDelegate(long nativeFontFamily) { + return sManager.getDelegate(nativeFontFamily); + } + + public static synchronized void setFontLocation(String fontLocation) { + sFontLocation = fontLocation; + for (FontFamily_Delegate fontFamily : sPostInitDelegate) { + fontFamily.init(); + } + sPostInitDelegate.clear(); + } + + public Font getFont(int style, boolean isCompact) { + FontInfo plainFont = null; + FontInfo styledFont = null; // Font matching the style but not isCompact + for (FontInfo font : mFonts) { + if (font.mStyle == style) { + if (font.mIsCompact == isCompact) { + return font.mFont; + } + styledFont = font; + } + if (font.mStyle == Font.PLAIN) { + if (plainFont == null) { + plainFont = font; + continue; + } + if (font.mIsCompact == isCompact) { + // Override the previous selection of plain font since we've found a better one. + plainFont = font; + } + } + } + if (styledFont != null) { + return styledFont.mFont; + } + + // No font with the mentioned style is found. Try to derive one. + if (plainFont != null && style > 0 && style < 4) { + styledFont = new FontInfo(); + styledFont.mFont = plainFont.mFont.deriveFont(style); + styledFont.mStyle = style; + styledFont.mIsCompact = plainFont.mIsCompact; + // Add the font to the list of fonts so that we don't have to derive it the next time. + mFonts.add(styledFont); + return styledFont.mFont; + } + return null; + } + + // ---- native methods ---- + + @LayoutlibDelegate + /*package*/ static long nCreateFamily() { + FontFamily_Delegate delegate = new FontFamily_Delegate(); + if (sFontLocation != null) { + delegate.init(); + } else { + sPostInitDelegate.add(delegate); + } + return sManager.addNewDelegate(delegate); + } + + @LayoutlibDelegate + /*package*/ static void nUnrefFamily(long nativePtr) { + sManager.removeJavaReferenceFor(nativePtr); + } + + @LayoutlibDelegate + /*package*/ static boolean nAddFont(long nativeFamily, String path) { + FontFamily_Delegate delegate = getDelegate(nativeFamily); + if (delegate != null) { + if (sFontLocation == null) { + delegate.mPath.add(path); + return true; + } + return delegate.addFont(path); + } + return false; + } + + private void init() { + for (String path : mPath) { + addFont(path); + } + mPath = null; + } + + private boolean addFont(String path) { + Font font = loadFont(path); + if (font == null) { + return false; + } + FontInfo fontInfo = new FontInfo(); + fontInfo.mFont = font; + addFontMetadata(fontInfo, path); + // TODO ensure that mFonts doesn't have the font with this style already. + mFonts.add(fontInfo); + return true; + } + + private static void addFontMetadata(FontInfo fontInfo, String path) { + int style = Font.PLAIN; + String fontName = path.substring(path.lastIndexOf('/'), path.length()); + if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) { + style = Font.BOLD | Font.ITALIC; + } else if (fontName.endsWith(FONT_SUFFIX_BOLD)) { + style = Font.BOLD; + } else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) { + style = Font.ITALIC; + } + fontInfo.mStyle = style; + + // Names of compact fonts end with UI-<style>.ttf. For example, NotoNakshUI-Regular.ttf. + // This should go away when this info is passed on by nAddFont(). + int hyphenIndex = fontName.lastIndexOf('-'); + fontInfo.mIsCompact = hyphenIndex > 0 && + fontName.substring(0, hyphenIndex).endsWith(FONT_SUBSTRING_COMPACT); + + } + + private static Font loadFont(String path) { + if (path.startsWith(SYSTEM_FONTS) ) { + String relativePath = path.substring(SYSTEM_FONTS.length()); + File f = new File(sFontLocation, relativePath); + + try { + return Font.createFont(Font.TRUETYPE_FONT, f); + } catch (Exception e) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, + String.format("Unable to load font %1$s", relativePath), + null /*throwable*/, null /*data*/); + } + } else { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.", + null /*throwable*/, null /*data*/); + } + + return null; + } +} diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 8862f5b..f42f48f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -203,6 +203,16 @@ public final class Matrix_Delegate { } @LayoutlibDelegate + /*package*/ static boolean native_isAffine(long native_object) { + Matrix_Delegate d = sManager.getDelegate(native_object); + if (d == null) { + return true; + } + + return (d.computeTypeMask() & kPerspective_Mask) == 0; + } + + @LayoutlibDelegate /*package*/ static boolean native_rectStaysRect(long native_object) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 83df745..911f4e7 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -53,7 +53,7 @@ import java.util.Locale; public class Paint_Delegate { /** - * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. + * Class associating a {@link Font} and its {@link java.awt.FontMetrics}. */ /*package*/ static final class FontInfo { Font mFont; @@ -66,8 +66,6 @@ public class Paint_Delegate { // ---- delegate helper data ---- private List<FontInfo> mFonts; - private final FontRenderContext mFontContext = new FontRenderContext( - new AffineTransform(), true, true); // ---- delegate data ---- private int mFlags; @@ -83,6 +81,7 @@ public class Paint_Delegate { private float mTextScaleX; private float mTextSkewX; private int mHintingMode = Paint.HINTING_ON; + private boolean mIsCompact = true; private Xfermode_Delegate mXfermode; private ColorFilter_Delegate mColorFilter; @@ -101,8 +100,7 @@ public class Paint_Delegate { } /** - * Returns the list of {@link Font} objects. The first item is the main font, the rest - * are fall backs for characters not present in the main font. + * Returns the list of {@link Font} objects. */ public List<FontInfo> getFonts() { return mFonts; @@ -437,12 +435,20 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static boolean isElegantTextHeight(Paint thisPaint) { - return false; + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + return delegate != null && !delegate.mIsCompact; } @LayoutlibDelegate /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) { - // TODO + // get the delegate from the native int. + Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); + if (delegate == null) { + return; + } + + delegate.mIsCompact = !elegant; } @LayoutlibDelegate @@ -621,7 +627,6 @@ public class Paint_Delegate { int inc = count > 0 ? 1 : -1; int measureIndex = 0; - float measureAcc = 0; for (int i = index; i != index + count; i += inc, measureIndex++) { int start, end; if (i < index) { @@ -640,7 +645,6 @@ public class Paint_Delegate { measuredWidth[measureIndex] = res; } - measureAcc += res; if (res > maxWidth) { // we should not return this char index, but since it's 0-based // and we need to return a count, we simply return measureIndex; @@ -818,7 +822,7 @@ public class Paint_Delegate { return filter; } - delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);; + delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter); // since none of those are supported, display a fidelity warning right away if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) { @@ -940,52 +944,17 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static int native_getTextWidths(long native_object, char[] text, int index, - int count, int bidiFlags, float[] widths) { - // get the delegate from the native int. - Paint_Delegate delegate = sManager.getDelegate(native_object); - if (delegate == null) { - return 0; - } - - if (delegate.mFonts.size() > 0) { - // FIXME: handle multi-char characters (see measureText) - float totalAdvance = 0; - for (int i = 0; i < count; i++) { - char c = text[i + index]; - boolean found = false; - for (FontInfo info : delegate.mFonts) { - if (info.mFont.canDisplay(c)) { - float adv = info.mMetrics.charWidth(c); - totalAdvance += adv; - if (widths != null) { - widths[i] = adv; - } - - found = true; - break; - } - } - - if (found == false) { - // no advance for this char. - if (widths != null) { - widths[i] = 0.f; - } - } - } - - return (int) totalAdvance; - } - - return 0; + /*package*/ static int native_getTextWidths(long native_object, long native_typeface, + char[] text, int index, int count, int bidiFlags, float[] widths) { + return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count, + index, count, bidiFlags, widths, 0); } @LayoutlibDelegate - /*package*/ static int native_getTextWidths(long native_object, String text, int start, - int end, int bidiFlags, float[] widths) { - return native_getTextWidths(native_object, text.toCharArray(), start, end - start, - bidiFlags, widths); + /*package*/ static int native_getTextWidths(long native_object, long native_typeface, + String text, int start, int end, int bidiFlags, float[] widths) { + return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start, + end - start, bidiFlags, widths); } @LayoutlibDelegate @@ -997,15 +966,20 @@ public class Paint_Delegate { @LayoutlibDelegate /*package*/ static float native_getTextRunAdvances(long native_object, + long native_typeface /*ignored*/, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex) { + // native_typeface is passed here since Framework's old implementation did not have the + // typeface object associated with the Paint. Since, we follow the new framework way, + // we store the typeface with the paint and use it directly. + if (advances != null) for (int i = advancesIndex; i< advancesIndex+count; i++) advances[i]=0; // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); - if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { + if (delegate == null) { return 0.f; } boolean isRtl = isRtl(flags); @@ -1017,7 +991,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static float native_getTextRunAdvances(long native_object, + /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex) { // FIXME: support contextStart and contextEnd @@ -1025,8 +999,8 @@ public class Paint_Delegate { char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); - return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart, - contextEnd - contextStart, flags, advances, advancesIndex); + return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count, + contextStart, contextEnd - contextStart, flags, advances, advancesIndex); } @LayoutlibDelegate @@ -1076,7 +1050,7 @@ public class Paint_Delegate { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(nativePaint); - if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { + if (delegate == null) { return; } delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds); @@ -1150,7 +1124,7 @@ public class Paint_Delegate { private void updateFontObject() { if (mTypeface != null) { // Get the fonts from the TypeFace object. - List<Font> fonts = mTypeface.getFonts(); + List<Font> fonts = mTypeface.getFonts(mIsCompact); // create new font objects as well as FontMetrics, based on the current text size // and skew info. diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java index 60cd157..ed8f3b4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java @@ -19,7 +19,6 @@ package android.graphics; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; -import com.android.layoutlib.bridge.impl.FontLoader; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.content.res.AssetManager; @@ -44,100 +43,67 @@ import java.util.List; */ public final class Typeface_Delegate { - private static final String SYSTEM_FONTS = "/system/fonts/"; + public static final String SYSTEM_FONTS = "/system/fonts/"; // ---- delegate manager ---- private static final DelegateManager<Typeface_Delegate> sManager = new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class); // ---- delegate helper data ---- - private static final String DEFAULT_FAMILY = "sans-serif"; - - private static FontLoader sFontLoader; - private static final List<Typeface_Delegate> sPostInitDelegate = - new ArrayList<Typeface_Delegate>(); + private static String sFontLocation; // ---- delegate data ---- - private final String mFamily; + private final long[] mFontFamilies; // the reference to FontFamily_Delegate. private int mStyle; - private List<Font> mFonts; + private static long sDefaultTypeface; // ---- Public Helper methods ---- - - public static synchronized void init(FontLoader fontLoader) { - sFontLoader = fontLoader; - - for (Typeface_Delegate delegate : sPostInitDelegate) { - delegate.init(); - } - sPostInitDelegate.clear(); + public static synchronized void setFontLocation(String fontLocation) { + sFontLocation = fontLocation; + FontFamily_Delegate.setFontLocation(fontLocation); } public static Typeface_Delegate getDelegate(long nativeTypeface) { return sManager.getDelegate(nativeTypeface); } - public static List<Font> getFonts(Typeface typeface) { - return getFonts(typeface.native_instance); - } - - public static List<Font> getFonts(long native_int) { - Typeface_Delegate delegate = sManager.getDelegate(native_int); - if (delegate == null) { - return null; + public List<Font> getFonts(boolean compact) { + List<Font> fonts = new ArrayList<Font>(mFontFamilies.length); + for (long fontFamily : mFontFamilies) { + FontFamily_Delegate ffd = FontFamily_Delegate.getDelegate(fontFamily); + if (ffd != null) { + Font font = ffd.getFont(mStyle, compact); + if (font != null) { + fonts.add(font); + } + } } - - return delegate.getFonts(); - } - - public List<Font> getFonts() { - return mFonts; + return fonts; } // ---- native methods ---- @LayoutlibDelegate /*package*/ static synchronized long nativeCreate(String familyName, int style) { - if (familyName == null) { - familyName = DEFAULT_FAMILY; - } - if (style < 0) { - style = Typeface.NORMAL; - } - - Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style); - if (sFontLoader != null) { - newDelegate.init(); - } else { - // font loader has not been initialized yet, add the delegate to a list of delegates - // to init when the font loader is initialized. - // There won't be any rendering before this happens anyway. - sPostInitDelegate.add(newDelegate); - } - - return sManager.addNewDelegate(newDelegate); + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Could not find font with family \"" + familyName + "\".", + null /*throwable*/, null /*data*/); + return 0; } @LayoutlibDelegate /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) { Typeface_Delegate delegate = sManager.getDelegate(native_instance); if (delegate == null) { - return 0; + delegate = sManager.getDelegate(sDefaultTypeface); } - - Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style); - if (sFontLoader != null) { - newDelegate.init(); - } else { - // font loader has not been initialized yet, add the delegate to a list of delegates - // to init when the font loader is initialized. - // There won't be any rendering before this happens anyway. - sPostInitDelegate.add(newDelegate); + if (delegate == null) { + return 0; } - return sManager.addNewDelegate(newDelegate); + return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style)); } @LayoutlibDelegate @@ -149,31 +115,15 @@ public final class Typeface_Delegate { @LayoutlibDelegate /*package*/ static synchronized long nativeCreateFromFile(String path) { - if (path.startsWith(SYSTEM_FONTS) ) { - String relativePath = path.substring(SYSTEM_FONTS.length()); - File f = new File(sFontLoader.getOsFontsLocation(), relativePath); - - try { - Font font = Font.createFont(Font.TRUETYPE_FONT, f); - if (font != null) { - Typeface_Delegate newDelegate = new Typeface_Delegate(font); - return sManager.addNewDelegate(newDelegate); - } - } catch (Exception e) { - Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, - String.format("Unable to load font %1$s", relativePath), - null /*throwable*/, null /*data*/); - } - } else { - Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, - "Typeface.createFromFile() can only work with platform fonts located in " + - SYSTEM_FONTS, - null /*throwable*/, null /*data*/); - } - + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Typeface.createFromFile() is not supported.,", null, null); + return 0; + } - // return a copy of the base font - return nativeCreate(null, 0); + @LayoutlibDelegate + /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) { + Typeface_Delegate delegate = new Typeface_Delegate(familyArray, Typeface.NORMAL); + return sManager.addNewDelegate(delegate); } @LayoutlibDelegate @@ -191,24 +141,21 @@ public final class Typeface_Delegate { return delegate.mStyle; } - // ---- Private delegate/helper methods ---- - - private Typeface_Delegate(String family, int style) { - mFamily = family; - mStyle = style; + @LayoutlibDelegate + /*package*/ static void nativeSetDefault(long native_instance) { + sDefaultTypeface = native_instance; } - private Typeface_Delegate(Font font) { - mFamily = font.getFamily(); - mStyle = Typeface.NORMAL; + @LayoutlibDelegate + /*package*/ static File getSystemFontConfigLocation() { + return new File(sFontLocation); + } - mFonts = sFontLoader.getFallbackFonts(mStyle); + // ---- Private delegate/helper methods ---- - // insert the font glyph first. - mFonts.add(0, font); + private Typeface_Delegate(long[] fontFamilies, int style) { + mFontFamilies = fontFamilies; + mStyle = style; } - private void init() { - mFonts = sFontLoader.getFont(mFamily, mStyle); - } } diff --git a/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java new file mode 100644 index 0000000..a193330 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java @@ -0,0 +1,49 @@ +/* + * 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.util; + +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import org.kxml2.io.KXmlParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +/** + * Delegate overriding some methods of android.util.Xml + * + * Through the layoutlib_create tool, the original methods of Xml have been replaced + * by calls to methods of the same name in this delegate class. + * + * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager} + * around to map int to instance of the delegate. + */ +public class Xml_Delegate { + + @LayoutlibDelegate + /*package*/ static XmlPullParser newPullParser() { + try { + KXmlParser parser = new KXmlParser(); + // The prebuilt kxml2 library with the IDE doesn't support DOCECL. +// parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + return parser; + } catch (XmlPullParserException e) { + throw new AssertionError(); + } + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index fa8050f..ffab4de 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -26,7 +26,6 @@ import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result.Status; import com.android.ide.common.rendering.api.SessionParams; -import com.android.layoutlib.bridge.impl.FontLoader; import com.android.layoutlib.bridge.impl.RenderDrawable; import com.android.layoutlib.bridge.impl.RenderSessionImpl; import com.android.layoutlib.bridge.util.DynamicIdMap; @@ -61,7 +60,7 @@ import java.util.concurrent.locks.ReentrantLock; /** * Main entry point of the LayoutLib Bridge. * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call - * {@link #createScene(SceneParams)} + * {@link #createSession(SessionParams)} */ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { @@ -147,8 +146,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { if (getClass() != obj.getClass()) return false; IntArray other = (IntArray) obj; - if (!Arrays.equals(mArray, other.mArray)) return false; - return true; + return Arrays.equals(mArray, other.mArray); } } @@ -251,14 +249,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { } // load the fonts. - FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath()); - if (fontLoader != null) { - Typeface_Delegate.init(fontLoader); - } else { - log.error(LayoutLog.TAG_BROKEN, - "Failed create FontLoader in layout lib.", null); - return false; - } + Typeface_Delegate.setFontLocation(fontLocation.getAbsolutePath()); // now parse com.android.internal.R (and only this one as android.R is a subset of // the internal version), and put the content in the maps. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java deleted file mode 100644 index cc7338a..0000000 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.layoutlib.bridge.impl; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import android.graphics.Typeface; - -import java.awt.Font; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * Provides {@link Font} object to the layout lib. - * <p/> - * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the - * fonts.xml file located alongside the ttf files. - */ -public final class FontLoader { - private static final String FONTS_SYSTEM = "system_fonts.xml"; - private static final String FONTS_VENDOR = "vendor_fonts.xml"; - private static final String FONTS_FALLBACK = "fallback_fonts.xml"; - - private static final String NODE_FAMILYSET = "familyset"; - private static final String NODE_FAMILY = "family"; - private static final String NODE_NAME = "name"; - private static final String NODE_FILE = "file"; - - private static final String ATTRIBUTE_VARIANT = "variant"; - private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant"; - private static final String FONT_SUFFIX_NONE = ".ttf"; - private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf"; - private static final String FONT_SUFFIX_BOLD = "-Bold.ttf"; - // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked - // separately. - private static final String FONT_SUFFIX_ITALIC = "Italic.ttf"; - private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf"; - - // This must match the values of Typeface styles so that we can use them for indices in this - // array. - private static final int[] AWT_STYLES = new int[] { - Font.PLAIN, - Font.BOLD, - Font.ITALIC, - Font.BOLD | Font.ITALIC - }; - private static int[] DERIVE_BOLD_ITALIC = new int[] { - Typeface.ITALIC, Typeface.BOLD, Typeface.NORMAL - }; - private static int[] DERIVE_ITALIC = new int[] { Typeface.NORMAL }; - private static int[] DERIVE_BOLD = new int[] { Typeface.NORMAL }; - - private static final List<FontInfo> mMainFonts = new ArrayList<FontInfo>(); - private static final List<FontInfo> mFallbackFonts = new ArrayList<FontInfo>(); - - private final String mOsFontsLocation; - - public static FontLoader create(String fontOsLocation) { - try { - SAXParserFactory parserFactory = SAXParserFactory.newInstance(); - parserFactory.setNamespaceAware(true); - - // parse the system fonts - FontHandler handler = parseFontFile(parserFactory, fontOsLocation, FONTS_SYSTEM); - List<FontInfo> systemFonts = handler.getFontList(); - - - // parse the fallback fonts - handler = parseFontFile(parserFactory, fontOsLocation, FONTS_FALLBACK); - List<FontInfo> fallbackFonts = handler.getFontList(); - - return new FontLoader(fontOsLocation, systemFonts, fallbackFonts); - } catch (ParserConfigurationException e) { - // return null below - } catch (SAXException e) { - // return null below - } catch (FileNotFoundException e) { - // return null below - } catch (IOException e) { - // return null below - } - - return null; - } - - private static FontHandler parseFontFile(SAXParserFactory parserFactory, - String fontOsLocation, String fontFileName) - throws ParserConfigurationException, SAXException, IOException, FileNotFoundException { - - SAXParser parser = parserFactory.newSAXParser(); - File f = new File(fontOsLocation, fontFileName); - - FontHandler definitionParser = new FontHandler( - fontOsLocation + File.separator); - parser.parse(new FileInputStream(f), definitionParser); - return definitionParser; - } - - private FontLoader(String fontOsLocation, - List<FontInfo> fontList, List<FontInfo> fallBackList) { - mOsFontsLocation = fontOsLocation; - mMainFonts.addAll(fontList); - mFallbackFonts.addAll(fallBackList); - } - - - public String getOsFontsLocation() { - return mOsFontsLocation; - } - - /** - * Returns a {@link Font} object given a family name and a style value (constant in - * {@link Typeface}). - * @param family the family name - * @param style a 1-item array containing the requested style. Based on the font being read - * the actual style may be different. The array contains the actual style after - * the method returns. - * @return the font object or null if no match could be found. - */ - public synchronized List<Font> getFont(String family, int style) { - List<Font> result = new ArrayList<Font>(); - - if (family == null) { - return result; - } - - - // get the font objects from the main list based on family. - for (FontInfo info : mMainFonts) { - if (info.families.contains(family)) { - result.add(info.font[style]); - break; - } - } - - // add all the fallback fonts for the given style - for (FontInfo info : mFallbackFonts) { - result.add(info.font[style]); - } - - return result; - } - - - public synchronized List<Font> getFallbackFonts(int style) { - List<Font> result = new ArrayList<Font>(); - // add all the fallback fonts - for (FontInfo info : mFallbackFonts) { - result.add(info.font[style]); - } - return result; - } - - - private final static class FontInfo { - final Font[] font = new Font[4]; // Matches the 4 type-face styles. - final Set<String> families; - - FontInfo() { - families = new HashSet<String>(); - } - } - - private final static class FontHandler extends DefaultHandler { - private final String mOsFontsLocation; - - private FontInfo mFontInfo = null; - private final StringBuilder mBuilder = new StringBuilder(); - private List<FontInfo> mFontList = new ArrayList<FontInfo>(); - private boolean isCompactFont = true; - - private FontHandler(String osFontsLocation) { - super(); - mOsFontsLocation = osFontsLocation; - } - - public List<FontInfo> getFontList() { - return mFontList; - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) - */ - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) - throws SAXException { - if (NODE_FAMILYSET.equals(localName)) { - mFontList = new ArrayList<FontInfo>(); - } else if (NODE_FAMILY.equals(localName)) { - if (mFontList != null) { - mFontInfo = null; - } - } else if (NODE_NAME.equals(localName)) { - if (mFontList != null && mFontInfo == null) { - mFontInfo = new FontInfo(); - } - } else if (NODE_FILE.equals(localName)) { - if (mFontList != null && mFontInfo == null) { - mFontInfo = new FontInfo(); - } - if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) { - isCompactFont = false; - } else { - isCompactFont = true; - } - } - - mBuilder.setLength(0); - - super.startElement(uri, localName, name, attributes); - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int) - */ - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - if (isCompactFont) { - mBuilder.append(ch, start, length); - } - } - - /* (non-Javadoc) - * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String) - */ - @Override - public void endElement(String uri, String localName, String name) throws SAXException { - if (NODE_FAMILY.equals(localName)) { - if (mFontInfo != null) { - // if has a normal font file, add to the list - if (mFontInfo.font[Typeface.NORMAL] != null) { - mFontList.add(mFontInfo); - - // create missing font styles, order is important. - if (mFontInfo.font[Typeface.BOLD_ITALIC] == null) { - computeDerivedFont(Typeface.BOLD_ITALIC, DERIVE_BOLD_ITALIC); - } - if (mFontInfo.font[Typeface.ITALIC] == null) { - computeDerivedFont(Typeface.ITALIC, DERIVE_ITALIC); - } - if (mFontInfo.font[Typeface.BOLD] == null) { - computeDerivedFont(Typeface.BOLD, DERIVE_BOLD); - } - } - - mFontInfo = null; - } - } else if (NODE_NAME.equals(localName)) { - // handle a new name for an existing Font Info - if (mFontInfo != null) { - String family = trimXmlWhitespaces(mBuilder.toString()); - mFontInfo.families.add(family); - } - } else if (NODE_FILE.equals(localName)) { - // handle a new file for an existing Font Info - if (isCompactFont && mFontInfo != null) { - String fileName = trimXmlWhitespaces(mBuilder.toString()); - Font font = getFont(fileName); - if (font != null) { - if (fileName.endsWith(FONT_SUFFIX_REGULAR)) { - mFontInfo.font[Typeface.NORMAL] = font; - } else if (fileName.endsWith(FONT_SUFFIX_BOLD)) { - mFontInfo.font[Typeface.BOLD] = font; - } else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) { - mFontInfo.font[Typeface.BOLD_ITALIC] = font; - } else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) { - mFontInfo.font[Typeface.ITALIC] = font; - } else if (fileName.endsWith(FONT_SUFFIX_NONE)) { - mFontInfo.font[Typeface.NORMAL] = font; - } - } - } - } - } - - private Font getFont(String fileName) { - try { - File file = new File(mOsFontsLocation, fileName); - if (file.exists()) { - return Font.createFont(Font.TRUETYPE_FONT, file); - } - } catch (Exception e) { - - } - - return null; - } - - private void computeDerivedFont( int toCompute, int[] basedOnList) { - for (int basedOn : basedOnList) { - if (mFontInfo.font[basedOn] != null) { - mFontInfo.font[toCompute] = - mFontInfo.font[basedOn].deriveFont(AWT_STYLES[toCompute]); - return; - } - } - - // we really shouldn't stop there. This means we don't have a NORMAL font... - assert false; - } - - private String trimXmlWhitespaces(String value) { - if (value == null) { - return null; - } - - // look for carriage return and replace all whitespace around it by just 1 space. - int index; - - while ((index = value.indexOf('\n')) != -1) { - // look for whitespace on each side - int left = index - 1; - while (left >= 0) { - if (Character.isWhitespace(value.charAt(left))) { - left--; - } else { - break; - } - } - - int right = index + 1; - int count = value.length(); - while (right < count) { - if (Character.isWhitespace(value.charAt(right))) { - right++; - } else { - break; - } - } - - // remove all between left and right (non inclusive) and replace by a single space. - String leftString = null; - if (left >= 0) { - leftString = value.substring(0, left + 1); - } - String rightString = null; - if (right < count) { - rightString = value.substring(right); - } - - if (leftString != null) { - value = leftString; - if (rightString != null) { - value += " " + rightString; - } - } else { - value = rightString != null ? rightString : ""; - } - } - - // now we un-escape the string - int length = value.length(); - char[] buffer = value.toCharArray(); - - for (int i = 0 ; i < length ; i++) { - if (buffer[i] == '\\') { - if (buffer[i+1] == 'n') { - // replace the char with \n - buffer[i+1] = '\n'; - } - - // offset the rest of the buffer since we go from 2 to 1 char - System.arraycopy(buffer, i+1, buffer, i, length - i - 1); - length--; - } - } - - return new String(buffer, 0, length); - } - - } -} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index bb72a1e..1f7a28e 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -125,15 +125,19 @@ public final class CreateInfo implements ICreateInfo { "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;", "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#resolveAttribute", + "android.content.res.Resources$Theme#resolveAttributes", "android.content.res.Resources#localeToLanguageTag", "android.content.res.AssetManager#newTheme", "android.content.res.AssetManager#deleteTheme", "android.content.res.AssetManager#applyThemeStyle", "android.content.res.TypedArray#getValueAt", + "android.content.res.TypedArray#obtain", "android.graphics.BitmapFactory#finishDecode", + "android.graphics.Typeface#getSystemFontConfigLocation", "android.os.Handler#sendMessageAtTime", "android.os.HandlerThread#run", "android.text.format.DateFormat#is24HourFormat", + "android.util.Xml#newPullParser", "android.view.Choreographer#getRefreshRate", "android.view.Display#updateDisplayInfoLocked", "android.view.Display#getWindowManager", @@ -170,6 +174,7 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.DiscretePathEffect", "android.graphics.DrawFilter", "android.graphics.EmbossMaskFilter", + "android.graphics.FontFamily", "android.graphics.LayerRasterizer", "android.graphics.LightingColorFilter", "android.graphics.LinearGradient", diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py index ea3dccc..aea3241 100755 --- a/tools/layoutlib/rename_font/build_font.py +++ b/tools/layoutlib/rename_font/build_font.py @@ -15,10 +15,10 @@ # limitations under the License. """ -Rename the PS name of all fonts in the input directory and copy them to the +Rename the PS name of all fonts in the input directories and copy them to the output directory. -Usage: build_font.py /path/to/input_fonts/ /path/to/output_fonts/ +Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/output_fonts/ """ @@ -30,50 +30,86 @@ import os from lxml import etree import shutil import glob +from multiprocessing import Pool + +# global variable +dest_dir = '/tmp' def main(argv): - if len(argv) != 2: - print "Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/" - sys.exit(1) - if not os.path.isdir(argv[0]): - print argv[0] + "is not a valid directory" - sys.exit(1) - if not os.path.isdir(argv[1]): - print argv[1] + "is not a valid directory" - sys.exit(1) + if len(argv) < 2: + sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/') + for directory in argv: + if not os.path.isdir(directory): + sys.exit(directory + ' is not a valid directory') + global dest_dir + dest_dir = argv[-1] + src_dirs = argv[:-1] cwd = os.getcwd() - os.chdir(argv[1]) + os.chdir(dest_dir) files = glob.glob('*') for filename in files: os.remove(filename) os.chdir(cwd) - for filename in os.listdir(argv[0]): - if not os.path.splitext(filename)[1].lower() == ".ttf": - shutil.copy(os.path.join(argv[0], filename), argv[1]) - continue - print os.path.join(argv[0], filename) - old_ttf_path = os.path.join(argv[0], filename) + input_fonts = list() + for src_dir in src_dirs: + for dirname, dirnames, filenames in os.walk(src_dir): + for filename in filenames: + input_path = os.path.join(dirname, filename) + extension = os.path.splitext(filename)[1].lower() + if (extension == '.ttf'): + input_fonts.append(input_path) + elif (extension == '.xml'): + shutil.copy(input_path, dest_dir) + if '.git' in dirnames: + # don't go into any .git directories. + dirnames.remove('.git') + # Create as many threads as the number of CPUs + pool = Pool(processes=None) + pool.map(convert_font, input_fonts) + + +class InvalidFontException(Exception): + pass + +def convert_font(input_path): + filename = os.path.basename(input_path) + print 'Converting font: ' + filename + # the path to the output file. The file name is the fontfilename.ttx + ttx_path = os.path.join(dest_dir, filename) + ttx_path = ttx_path[:-1] + 'x' + try: # run ttx to generate an xml file in the output folder which represents all # its info - ttx_args = ["-d", argv[1], old_ttf_path] + ttx_args = ['-q', '-d', dest_dir, input_path] ttx.main(ttx_args) - # the path to the output file. The file name is the fontfilename.ttx - ttx_path = os.path.join(argv[1], filename) - ttx_path = ttx_path[:-1] + "x" # now parse the xml file to change its PS name. tree = etree.parse(ttx_path) encoding = tree.docinfo.encoding root = tree.getroot() for name in root.iter('name'): [old_ps_name, version] = get_font_info(name) - new_ps_name = old_ps_name + version - update_name(name, new_ps_name) + if old_ps_name is not None and version is not None: + new_ps_name = old_ps_name + version + update_name(name, new_ps_name) tree.write(ttx_path, xml_declaration=True, encoding=encoding ) # generate the udpated font now. - ttx_args = ["-d", argv[1], ttx_path] + ttx_args = ['-q', '-d', dest_dir, ttx_path] ttx.main(ttx_args) - # delete the temp ttx file. + except InvalidFontException: + # In case of invalid fonts, we exit. + print filename + ' is not a valid font' + raise + except Exception as e: + print 'Error converting font: ' + filename + print e + # Some fonts are too big to be handled by the ttx library. + # Just copy paste them. + shutil.copy(input_path, dest_dir) + try: + # delete the temp ttx file is it exists. os.remove(ttx_path) + except OSError: + pass def get_font_info(tag): ps_name = None @@ -85,19 +121,17 @@ def get_font_info(tag): if namerecord.attrib['nameID'] == '6': if ps_name is not None: if not sanitize(namerecord.text) == ps_name: - sys.exit('found multiple possibilities of the font name') + raise InvalidFontException('found multiple possibilities of the font name') else: ps_name = sanitize(namerecord.text) # nameID=5 means the font version if namerecord.attrib['nameID'] == '5': if ps_version is not None: if not ps_version == get_version(namerecord.text): - sys.exit('found multiple possibilities of the font version') + raise InvalidFontException('found multiple possibilities of the font version') else: ps_version = get_version(namerecord.text) - if ps_name is not None and ps_version is not None: - return [ps_name, ps_version] - sys.exit('didn\'t find the font name or version') + return [ps_name, ps_version] def update_name(tag, name): @@ -110,11 +144,11 @@ def sanitize(string): return re.sub(r'[^\w-]+', '', string) def get_version(string): - # The string must begin with "Version n.nn " + # The string must begin with 'Version n.nn ' # to extract n.nn, we return the second entry in the split strings. string = string.strip() - if not string.startswith("Version "): - sys.exit('mal-formed font version') + if not string.startswith('Version '): + raise InvalidFontException('mal-formed font version') return sanitize(string.split()[1]) if __name__ == '__main__': 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/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java index 08b430f..090ac56 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java @@ -25,13 +25,18 @@ import java.util.Set; import java.util.Iterator; import java.util.Map; +/** + * A class representing a Wi-Fi Passpoint credential. + */ public class WifiPasspointCredential implements Parcelable { private final static String TAG = "PasspointCredential"; + private String mWifiTreePath; private String mWifiSPFQDN; private String mCredentialName; private String mUpdateIdentifier; private String mSubscriptionUpdateMethod; + private WifiEnterpriseConfig mEnterpriseConfig; private String mType; private String mInnerMethod; private String mCertType; @@ -88,17 +93,16 @@ public class WifiPasspointCredential implements Parcelable { /** * 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 @@ -294,11 +298,21 @@ public class WifiPasspointCredential implements Parcelable { } /** - * 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 */ @@ -311,10 +325,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; } @@ -325,10 +336,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; } @@ -344,26 +352,31 @@ 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) { @@ -374,14 +387,22 @@ public class WifiPasspointCredential implements Parcelable { } /** - * 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() { 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; diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java index ee4dc5a..ebaa8c9 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java @@ -443,10 +443,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 */ |
