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 package com.android.internal.telephony.euicc; 17 18 import android.annotation.IntDef; 19 import android.annotation.Nullable; 20 import android.app.PendingIntent; 21 import android.os.Binder; 22 import android.os.Bundle; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.service.euicc.EuiccService; 26 import android.telephony.euicc.DownloadableSubscription; 27 import android.telephony.euicc.EuiccManager; 28 import android.text.TextUtils; 29 import android.util.Log; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 36 /** 37 * Representation of an {@link EuiccController} operation which failed with a resolvable error. 38 * 39 * <p>This class tracks the operation which failed and the reason for failure. Once the error is 40 * resolved, the operation can be resumed with {@link #continueOperation}. 41 */ 42 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 43 public class EuiccOperation implements Parcelable { 44 private static final String TAG = "EuiccOperation"; 45 46 public static final Creator<EuiccOperation> CREATOR = new Creator<EuiccOperation>() { 47 @Override 48 public EuiccOperation createFromParcel(Parcel in) { 49 return new EuiccOperation(in); 50 } 51 52 @Override 53 public EuiccOperation[] newArray(int size) { 54 return new EuiccOperation[size]; 55 } 56 }; 57 58 @VisibleForTesting 59 @Retention(RetentionPolicy.SOURCE) 60 @IntDef({ 61 ACTION_GET_METADATA_DEACTIVATE_SIM, 62 ACTION_DOWNLOAD_DEACTIVATE_SIM, 63 ACTION_DOWNLOAD_NO_PRIVILEGES, 64 ACTION_DOWNLOAD_CONFIRMATION_CODE, 65 }) 66 @interface Action {} 67 68 @VisibleForTesting 69 static final int ACTION_GET_METADATA_DEACTIVATE_SIM = 1; 70 @VisibleForTesting 71 static final int ACTION_DOWNLOAD_DEACTIVATE_SIM = 2; 72 @VisibleForTesting 73 static final int ACTION_DOWNLOAD_NO_PRIVILEGES = 3; 74 @VisibleForTesting 75 static final int ACTION_GET_DEFAULT_LIST_DEACTIVATE_SIM = 4; 76 @VisibleForTesting 77 static final int ACTION_SWITCH_DEACTIVATE_SIM = 5; 78 @VisibleForTesting 79 static final int ACTION_SWITCH_NO_PRIVILEGES = 6; 80 @VisibleForTesting 81 static final int ACTION_DOWNLOAD_CONFIRMATION_CODE = 7; 82 83 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 84 public final @Action int mAction; 85 86 private final long mCallingToken; 87 88 @Nullable 89 private final DownloadableSubscription mDownloadableSubscription; 90 private final int mSubscriptionId; 91 private final boolean mSwitchAfterDownload; 92 @Nullable 93 private final String mCallingPackage; 94 95 /** 96 * {@link EuiccManager#getDownloadableSubscriptionMetadata} failed with 97 * {@link EuiccService#RESULT_MUST_DEACTIVATE_SIM}. 98 */ forGetMetadataDeactivateSim(long callingToken, DownloadableSubscription subscription, String callingPackage)99 public static EuiccOperation forGetMetadataDeactivateSim(long callingToken, 100 DownloadableSubscription subscription, String callingPackage) { 101 return new EuiccOperation(ACTION_GET_METADATA_DEACTIVATE_SIM, callingToken, 102 subscription, 0 /* subscriptionId */, false /* switchAfterDownload */, 103 callingPackage); 104 } 105 106 /** 107 * {@link EuiccManager#downloadSubscription} failed with a mustDeactivateSim error. Should only 108 * be used for privileged callers; for unprivileged callers, use 109 * {@link #forDownloadNoPrivileges} to avoid a double prompt. 110 */ forDownloadDeactivateSim(long callingToken, DownloadableSubscription subscription, boolean switchAfterDownload, String callingPackage)111 public static EuiccOperation forDownloadDeactivateSim(long callingToken, 112 DownloadableSubscription subscription, boolean switchAfterDownload, 113 String callingPackage) { 114 return new EuiccOperation(ACTION_DOWNLOAD_DEACTIVATE_SIM, callingToken, 115 subscription, 0 /* subscriptionId */, switchAfterDownload, callingPackage); 116 } 117 118 /** 119 * {@link EuiccManager#downloadSubscription} failed because the calling app does not have 120 * permission to manage the current active subscription, or because we cannot determine the 121 * privileges without deactivating the current SIM first. 122 */ forDownloadNoPrivileges(long callingToken, DownloadableSubscription subscription, boolean switchAfterDownload, String callingPackage)123 public static EuiccOperation forDownloadNoPrivileges(long callingToken, 124 DownloadableSubscription subscription, boolean switchAfterDownload, 125 String callingPackage) { 126 return new EuiccOperation(ACTION_DOWNLOAD_NO_PRIVILEGES, callingToken, 127 subscription, 0 /* subscriptionId */, switchAfterDownload, callingPackage); 128 } 129 130 /** 131 * {@link EuiccManager#downloadSubscription} failed with 132 * {@link EuiccService#RESULT_NEED_CONFIRMATION_CODE} error. 133 */ forDownloadConfirmationCode(long callingToken, DownloadableSubscription subscription, boolean switchAfterDownload, String callingPackage)134 public static EuiccOperation forDownloadConfirmationCode(long callingToken, 135 DownloadableSubscription subscription, boolean switchAfterDownload, 136 String callingPackage) { 137 return new EuiccOperation(ACTION_DOWNLOAD_CONFIRMATION_CODE, callingToken, 138 subscription, 0 /* subscriptionId */, switchAfterDownload, callingPackage); 139 } 140 forGetDefaultListDeactivateSim(long callingToken, String callingPackage)141 static EuiccOperation forGetDefaultListDeactivateSim(long callingToken, String callingPackage) { 142 return new EuiccOperation(ACTION_GET_DEFAULT_LIST_DEACTIVATE_SIM, callingToken, 143 null /* downloadableSubscription */, 0 /* subscriptionId */, 144 false /* switchAfterDownload */, callingPackage); 145 } 146 forSwitchDeactivateSim(long callingToken, int subscriptionId, String callingPackage)147 static EuiccOperation forSwitchDeactivateSim(long callingToken, int subscriptionId, 148 String callingPackage) { 149 return new EuiccOperation(ACTION_SWITCH_DEACTIVATE_SIM, callingToken, 150 null /* downloadableSubscription */, subscriptionId, 151 false /* switchAfterDownload */, callingPackage); 152 } 153 forSwitchNoPrivileges(long callingToken, int subscriptionId, String callingPackage)154 static EuiccOperation forSwitchNoPrivileges(long callingToken, int subscriptionId, 155 String callingPackage) { 156 return new EuiccOperation(ACTION_SWITCH_NO_PRIVILEGES, callingToken, 157 null /* downloadableSubscription */, subscriptionId, 158 false /* switchAfterDownload */, callingPackage); 159 } 160 EuiccOperation(@ction int action, long callingToken, @Nullable DownloadableSubscription downloadableSubscription, int subscriptionId, boolean switchAfterDownload, String callingPackage)161 EuiccOperation(@Action int action, 162 long callingToken, 163 @Nullable DownloadableSubscription downloadableSubscription, 164 int subscriptionId, 165 boolean switchAfterDownload, 166 String callingPackage) { 167 mAction = action; 168 mCallingToken = callingToken; 169 mDownloadableSubscription = downloadableSubscription; 170 mSubscriptionId = subscriptionId; 171 mSwitchAfterDownload = switchAfterDownload; 172 mCallingPackage = callingPackage; 173 } 174 EuiccOperation(Parcel in)175 EuiccOperation(Parcel in) { 176 mAction = in.readInt(); 177 mCallingToken = in.readLong(); 178 mDownloadableSubscription = in.readTypedObject(DownloadableSubscription.CREATOR); 179 mSubscriptionId = in.readInt(); 180 mSwitchAfterDownload = in.readBoolean(); 181 mCallingPackage = in.readString(); 182 } 183 184 @Override writeToParcel(Parcel dest, int flags)185 public void writeToParcel(Parcel dest, int flags) { 186 dest.writeInt(mAction); 187 dest.writeLong(mCallingToken); 188 dest.writeTypedObject(mDownloadableSubscription, flags); 189 dest.writeInt(mSubscriptionId); 190 dest.writeBoolean(mSwitchAfterDownload); 191 dest.writeString(mCallingPackage); 192 } 193 194 /** 195 * Resume this operation based on the results of the resolution activity. 196 * 197 * @param resolutionExtras The resolution extras as provided to 198 * {@link EuiccManager#continueOperation}. 199 * @param callbackIntent The callback intent to trigger after the operation completes. 200 */ continueOperation(Bundle resolutionExtras, PendingIntent callbackIntent)201 public void continueOperation(Bundle resolutionExtras, PendingIntent callbackIntent) { 202 // Restore the identity of the caller. We should err on the side of caution and redo any 203 // permission checks before continuing with the operation in case the caller state has 204 // changed. Resolution flows can re-clear the identity if required. 205 Binder.restoreCallingIdentity(mCallingToken); 206 207 switch (mAction) { 208 case ACTION_GET_METADATA_DEACTIVATE_SIM: 209 resolvedGetMetadataDeactivateSim( 210 resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT), 211 callbackIntent); 212 break; 213 case ACTION_DOWNLOAD_DEACTIVATE_SIM: 214 resolvedDownloadDeactivateSim( 215 resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT), 216 callbackIntent); 217 break; 218 case ACTION_DOWNLOAD_NO_PRIVILEGES: 219 resolvedDownloadNoPrivileges( 220 resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT), 221 callbackIntent); 222 break; 223 case ACTION_DOWNLOAD_CONFIRMATION_CODE: 224 resolvedDownloadConfirmationCode( 225 resolutionExtras.getString(EuiccService.EXTRA_RESOLUTION_CONFIRMATION_CODE), 226 callbackIntent); 227 break; 228 case ACTION_GET_DEFAULT_LIST_DEACTIVATE_SIM: 229 resolvedGetDefaultListDeactivateSim( 230 resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT), 231 callbackIntent); 232 break; 233 case ACTION_SWITCH_DEACTIVATE_SIM: 234 resolvedSwitchDeactivateSim( 235 resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT), 236 callbackIntent); 237 break; 238 case ACTION_SWITCH_NO_PRIVILEGES: 239 resolvedSwitchNoPrivileges( 240 resolutionExtras.getBoolean(EuiccService.EXTRA_RESOLUTION_CONSENT), 241 callbackIntent); 242 break; 243 default: 244 Log.wtf(TAG, "Unknown action: " + mAction); 245 break; 246 } 247 } 248 resolvedGetMetadataDeactivateSim( boolean consent, PendingIntent callbackIntent)249 private void resolvedGetMetadataDeactivateSim( 250 boolean consent, PendingIntent callbackIntent) { 251 if (consent) { 252 // User has consented; perform the lookup, but this time, tell the LPA to deactivate any 253 // required active SIMs. 254 EuiccController.get().getDownloadableSubscriptionMetadata( 255 mDownloadableSubscription, 256 true /* forceDeactivateSim */, 257 mCallingPackage, 258 callbackIntent); 259 } else { 260 // User has not consented; fail the operation. 261 fail(callbackIntent); 262 } 263 } 264 resolvedDownloadDeactivateSim( boolean consent, PendingIntent callbackIntent)265 private void resolvedDownloadDeactivateSim( 266 boolean consent, PendingIntent callbackIntent) { 267 if (consent) { 268 // User has consented; perform the download, but this time, tell the LPA to deactivate 269 // any required active SIMs. 270 EuiccController.get().downloadSubscription( 271 mDownloadableSubscription, 272 mSwitchAfterDownload, 273 mCallingPackage, 274 true /* forceDeactivateSim */, 275 callbackIntent); 276 } else { 277 // User has not consented; fail the operation. 278 fail(callbackIntent); 279 } 280 } 281 resolvedDownloadNoPrivileges(boolean consent, PendingIntent callbackIntent)282 private void resolvedDownloadNoPrivileges(boolean consent, PendingIntent callbackIntent) { 283 if (consent) { 284 // User has consented; perform the download with full privileges. 285 long token = Binder.clearCallingIdentity(); 286 try { 287 // Note: We turn on "forceDeactivateSim" here under the assumption that the 288 // privilege prompt should also cover permission to deactivate an active SIM, as 289 // the privilege prompt makes it clear that we're switching from the current 290 // carrier. 291 EuiccController.get().downloadSubscriptionPrivileged( 292 token, 293 mDownloadableSubscription, 294 mSwitchAfterDownload, 295 true /* forceDeactivateSim */, 296 mCallingPackage, 297 callbackIntent); 298 } finally { 299 Binder.restoreCallingIdentity(token); 300 } 301 } else { 302 // User has not consented; fail the operation. 303 fail(callbackIntent); 304 } 305 } 306 resolvedDownloadConfirmationCode(String confirmationCode, PendingIntent callbackIntent)307 private void resolvedDownloadConfirmationCode(String confirmationCode, 308 PendingIntent callbackIntent) { 309 if (TextUtils.isEmpty(confirmationCode)) { 310 fail(callbackIntent); 311 } else { 312 mDownloadableSubscription.setConfirmationCode(confirmationCode); 313 EuiccController.get() 314 .downloadSubscription( 315 mDownloadableSubscription, 316 mSwitchAfterDownload, 317 mCallingPackage, 318 true /* forceDeactivateSim */, 319 callbackIntent); 320 } 321 } 322 resolvedGetDefaultListDeactivateSim( boolean consent, PendingIntent callbackIntent)323 private void resolvedGetDefaultListDeactivateSim( 324 boolean consent, PendingIntent callbackIntent) { 325 if (consent) { 326 // User has consented; perform the lookup, but this time, tell the LPA to deactivate any 327 // required active SIMs. 328 EuiccController.get().getDefaultDownloadableSubscriptionList( 329 true /* forceDeactivateSim */, mCallingPackage, callbackIntent); 330 } else { 331 // User has not consented; fail the operation. 332 fail(callbackIntent); 333 } 334 } 335 resolvedSwitchDeactivateSim( boolean consent, PendingIntent callbackIntent)336 private void resolvedSwitchDeactivateSim( 337 boolean consent, PendingIntent callbackIntent) { 338 if (consent) { 339 // User has consented; perform the switch, but this time, tell the LPA to deactivate any 340 // required active SIMs. 341 EuiccController.get().switchToSubscription( 342 mSubscriptionId, 343 true /* forceDeactivateSim */, 344 mCallingPackage, 345 callbackIntent); 346 } else { 347 // User has not consented; fail the operation. 348 fail(callbackIntent); 349 } 350 } 351 resolvedSwitchNoPrivileges(boolean consent, PendingIntent callbackIntent)352 private void resolvedSwitchNoPrivileges(boolean consent, PendingIntent callbackIntent) { 353 if (consent) { 354 // User has consented; perform the switch with full privileges. 355 long token = Binder.clearCallingIdentity(); 356 try { 357 // Note: We turn on "forceDeactivateSim" here under the assumption that the 358 // privilege prompt should also cover permission to deactivate an active SIM, as 359 // the privilege prompt makes it clear that we're switching from the current 360 // carrier. Also note that in practice, we'd need to deactivate the active SIM to 361 // even reach this point, because we cannot fetch the metadata needed to check the 362 // privileges without doing so. 363 EuiccController.get().switchToSubscriptionPrivileged( 364 token, 365 mSubscriptionId, 366 true /* forceDeactivateSim */, 367 mCallingPackage, 368 callbackIntent); 369 } finally { 370 Binder.restoreCallingIdentity(token); 371 } 372 } else { 373 // User has not consented; fail the operation. 374 fail(callbackIntent); 375 } 376 } 377 fail(PendingIntent callbackIntent)378 private static void fail(PendingIntent callbackIntent) { 379 EuiccController.get().sendResult( 380 callbackIntent, 381 EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 382 null /* extrasIntent */); 383 } 384 385 @Override describeContents()386 public int describeContents() { 387 return 0; 388 } 389 } 390