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