1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.timezone; 18 19 import android.annotation.IntDef; 20 import android.content.Context; 21 import android.os.Handler; 22 import android.os.ParcelFileDescriptor; 23 import android.os.RemoteException; 24 import android.os.ServiceManager; 25 import android.util.Log; 26 27 import java.io.IOException; 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.util.Arrays; 31 32 /** 33 * The interface through which a time zone update application interacts with the Android system 34 * to handle time zone rule updates. 35 * 36 * <p>This interface is intended for use with the default APK-based time zone rules update 37 * application but it can also be used by OEMs if that mechanism is turned off using configuration. 38 * All callers must possess the {@link android.Manifest.permission#UPDATE_TIME_ZONE_RULES} system 39 * permission unless otherwise stated. 40 * 41 * <p>When using the default mechanism, when properly configured the Android system will send a 42 * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent with a 43 * {@link RulesUpdaterContract#EXTRA_CHECK_TOKEN} extra to the time zone rules updater application 44 * when it detects that it or the OEM's APK containing time zone rules data has been modified. The 45 * updater application is then responsible for calling one of 46 * {@link #requestInstall(ParcelFileDescriptor, byte[], Callback)}, 47 * {@link #requestUninstall(byte[], Callback)} or 48 * {@link #requestNothing(byte[], boolean)}, indicating, respectively, whether a new time zone rules 49 * distro should be installed, the current distro should be uninstalled, or there is nothing to do 50 * (or that the correct operation could not be determined due to an error). In each case the updater 51 * must pass the {@link RulesUpdaterContract#EXTRA_CHECK_TOKEN} value it received from the intent 52 * back so the system in the {@code checkToken} parameter. 53 * 54 * <p>If OEMs want to handle their own time zone rules updates, perhaps via a server-side component 55 * rather than an APK, then they should disable the default triggering mechanism in config and are 56 * responsible for triggering their own update checks / installs / uninstalls. In this case the 57 * "check token" parameter can be left null and there is never any need to call 58 * {@link #requestNothing(byte[], boolean)}. 59 * 60 * <p>OEMs should not mix the default mechanism and their own as this could lead to conflicts and 61 * unnecessary checks being triggered. 62 * 63 * <p>Applications obtain this using {@link android.app.Activity#getSystemService(String)} with 64 * {@link Context#TIME_ZONE_RULES_MANAGER_SERVICE}. 65 * @hide 66 */ 67 public final class RulesManager { 68 private static final String TAG = "timezone.RulesManager"; 69 private static final boolean DEBUG = false; 70 71 /** 72 * The action of the intent that the Android system will broadcast when a time zone rules update 73 * operation has been successfully staged (i.e. to be applied next reboot) or unstaged. 74 * 75 * <p>See {@link #EXTRA_OPERATION_STAGED} 76 * 77 * <p>This is a protected intent that can only be sent by the system. 78 */ 79 public static final String ACTION_RULES_UPDATE_OPERATION = 80 "com.android.intent.action.timezone.RULES_UPDATE_OPERATION"; 81 82 /** 83 * The key for a boolean extra for the {@link #ACTION_RULES_UPDATE_OPERATION} intent used to 84 * indicate whether the operation was a "stage" or an "unstage". 85 */ 86 public static final String EXTRA_OPERATION_STAGED = "staged"; 87 88 @Retention(RetentionPolicy.SOURCE) 89 @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = { 90 SUCCESS, 91 ERROR_UNKNOWN_FAILURE, 92 ERROR_OPERATION_IN_PROGRESS 93 }) 94 public @interface ResultCode {} 95 96 /** 97 * Indicates that an operation succeeded. 98 */ 99 public static final int SUCCESS = 0; 100 101 /** 102 * Indicates that an install/uninstall cannot be initiated because there is one already in 103 * progress. 104 */ 105 public static final int ERROR_OPERATION_IN_PROGRESS = 1; 106 107 /** 108 * Indicates an install / uninstall did not fully succeed for an unknown reason. 109 */ 110 public static final int ERROR_UNKNOWN_FAILURE = 2; 111 112 private final Context mContext; 113 private final IRulesManager mIRulesManager; 114 RulesManager(Context context)115 public RulesManager(Context context) { 116 mContext = context; 117 mIRulesManager = IRulesManager.Stub.asInterface( 118 ServiceManager.getService(Context.TIME_ZONE_RULES_MANAGER_SERVICE)); 119 } 120 121 /** 122 * Returns information about the current time zone rules state such as the IANA version of 123 * the system and any currently installed distro. This method allows clients to determine the 124 * current device state, perhaps to see if it can be improved; for example by passing the 125 * information to a server that may provide a new distro for download. 126 * 127 * <p>Callers must possess the {@link android.Manifest.permission#QUERY_TIME_ZONE_RULES} system 128 * permission. 129 */ getRulesState()130 public RulesState getRulesState() { 131 try { 132 logDebug("mIRulesManager.getRulesState()"); 133 RulesState rulesState = mIRulesManager.getRulesState(); 134 logDebug("mIRulesManager.getRulesState() returned " + rulesState); 135 return rulesState; 136 } catch (RemoteException e) { 137 throw e.rethrowFromSystemServer(); 138 } 139 } 140 141 /** 142 * Requests installation of the supplied distro. The distro must have been checked for integrity 143 * by the caller or have been received via a trusted mechanism. 144 * 145 * @param distroFileDescriptor the file descriptor for the distro 146 * @param checkToken an optional token provided if the install was triggered in response to a 147 * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent 148 * @param callback the {@link Callback} to receive callbacks related to the installation 149 * @return {@link #SUCCESS} if the installation will be attempted 150 */ 151 @ResultCode requestInstall( ParcelFileDescriptor distroFileDescriptor, byte[] checkToken, Callback callback)152 public int requestInstall( 153 ParcelFileDescriptor distroFileDescriptor, byte[] checkToken, Callback callback) 154 throws IOException { 155 156 ICallback iCallback = new CallbackWrapper(mContext, callback); 157 try { 158 logDebug("mIRulesManager.requestInstall()"); 159 return mIRulesManager.requestInstall(distroFileDescriptor, checkToken, iCallback); 160 } catch (RemoteException e) { 161 throw e.rethrowFromSystemServer(); 162 } 163 } 164 165 /** 166 * Requests uninstallation of the currently installed distro (leaving the device with no 167 * distro installed). 168 * 169 * @param checkToken an optional token provided if the uninstall was triggered in response to a 170 * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent 171 * @param callback the {@link Callback} to receive callbacks related to the uninstall 172 * @return {@link #SUCCESS} if the uninstallation will be attempted 173 */ 174 @ResultCode requestUninstall(byte[] checkToken, Callback callback)175 public int requestUninstall(byte[] checkToken, Callback callback) { 176 ICallback iCallback = new CallbackWrapper(mContext, callback); 177 try { 178 logDebug("mIRulesManager.requestUninstall()"); 179 return mIRulesManager.requestUninstall(checkToken, iCallback); 180 } catch (RemoteException e) { 181 throw e.rethrowFromSystemServer(); 182 } 183 } 184 185 /* 186 * We wrap incoming binder calls with a private class implementation that 187 * redirects them into main-thread actions. This serializes the backup 188 * progress callbacks nicely within the usual main-thread lifecycle pattern. 189 */ 190 private class CallbackWrapper extends ICallback.Stub { 191 final Handler mHandler; 192 final Callback mCallback; 193 CallbackWrapper(Context context, Callback callback)194 CallbackWrapper(Context context, Callback callback) { 195 mCallback = callback; 196 mHandler = new Handler(context.getMainLooper()); 197 } 198 199 // Binder calls into this object just enqueue on the main-thread handler 200 @Override onFinished(int status)201 public void onFinished(int status) { 202 logDebug("mCallback.onFinished(status), status=" + status); 203 mHandler.post(() -> mCallback.onFinished(status)); 204 } 205 } 206 207 /** 208 * Requests the system does not modify the currently installed time zone distro, if any. This 209 * method records the fact that a time zone check operation triggered by the system is now 210 * complete and there was nothing to do. The token passed should be the one presented when the 211 * check was triggered. 212 * 213 * <p>Note: Passing {@code success == false} may result in more checks being triggered. Clients 214 * should be careful not to pass false if the failure is unlikely to resolve by itself. 215 * 216 * @param checkToken an optional token provided if the install was triggered in response to a 217 * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent 218 * @param succeeded true if the check was successful, false if it was not successful but may 219 * succeed if it is retried 220 */ requestNothing(byte[] checkToken, boolean succeeded)221 public void requestNothing(byte[] checkToken, boolean succeeded) { 222 try { 223 logDebug("mIRulesManager.requestNothing() with token=" + Arrays.toString(checkToken)); 224 mIRulesManager.requestNothing(checkToken, succeeded); 225 } catch (RemoteException e) { 226 throw e.rethrowFromSystemServer(); 227 } 228 } 229 logDebug(String msg)230 static void logDebug(String msg) { 231 if (DEBUG) { 232 Log.v(TAG, msg); 233 } 234 } 235 } 236