• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 com.android.safetycenter.pendingintents;
18 
19 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
20 import static android.os.Build.VERSION_CODES.TIRAMISU;
21 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
22 
23 import android.app.ActivityOptions;
24 import android.app.PendingIntent;
25 import android.util.Log;
26 
27 import androidx.annotation.Nullable;
28 import androidx.annotation.RequiresApi;
29 import androidx.annotation.RequiresPermission;
30 
31 import com.android.modules.utils.build.SdkLevel;
32 
33 /** A class to facilitate sending {@link PendingIntent}s associated with Safety Center. */
34 @RequiresApi(TIRAMISU)
35 public final class PendingIntentSender {
36 
PendingIntentSender()37     private PendingIntentSender() {}
38 
39     private static final String TAG = "PendingIntentSender";
40 
41     /**
42      * Sends a {@link PendingIntent}.
43      *
44      * <p>On U+, launching activities in the background is opt-in by using {@link
45      * ActivityOptions#MODE_BACKGROUND_ACTIVITY_START_ALLOWED}. This call opts-in this behavior as
46      * the Safety Center intents come from trusted sources and are used for navigation purposes.
47      */
send(PendingIntent pendingIntent)48     public static void send(PendingIntent pendingIntent) throws PendingIntent.CanceledException {
49         send(pendingIntent, createActivityOptions(pendingIntent, /* launchTaskId= */ null));
50     }
51 
52     /** Same as {@link #send(PendingIntent)} but with the given optional {@code launchTaskId}. */
53     @RequiresPermission(START_TASKS_FROM_RECENTS)
send(PendingIntent pendingIntent, @Nullable Integer launchTaskId)54     public static void send(PendingIntent pendingIntent, @Nullable Integer launchTaskId)
55             throws PendingIntent.CanceledException {
56         send(pendingIntent, createActivityOptions(pendingIntent, launchTaskId));
57     }
58 
send(PendingIntent pendingIntent, @Nullable ActivityOptions activityOptions)59     private static void send(PendingIntent pendingIntent, @Nullable ActivityOptions activityOptions)
60             throws PendingIntent.CanceledException {
61         if (activityOptions == null) {
62             pendingIntent.send();
63             return;
64         }
65         pendingIntent.send(
66                 /* context= */ null,
67                 /* code= */ 0,
68                 /* intent= */ null,
69                 /* onFinished= */ null,
70                 /* handler= */ null,
71                 /* requiredPermission= */ null,
72                 activityOptions.toBundle());
73     }
74 
75     /**
76      * Same as {@link #send(PendingIntent)} but returns whether the call was successful instead of
77      * throwing a {@link PendingIntent#CanceledException}.
78      */
trySend(PendingIntent pendingIntent)79     public static boolean trySend(PendingIntent pendingIntent) {
80         return trySend(
81                 pendingIntent, createActivityOptions(pendingIntent, /* launchTaskId= */ null));
82     }
83 
84     /**
85      * Same as {@link #send(PendingIntent, Integer)} but returns whether the call was successful
86      * instead of throwing a {@link PendingIntent#CanceledException}.
87      */
88     @RequiresPermission(START_TASKS_FROM_RECENTS)
trySend(PendingIntent pendingIntent, @Nullable Integer launchTaskId)89     public static boolean trySend(PendingIntent pendingIntent, @Nullable Integer launchTaskId) {
90         return trySend(pendingIntent, createActivityOptions(pendingIntent, launchTaskId));
91     }
92 
trySend( PendingIntent pendingIntent, @Nullable ActivityOptions activityOptions)93     private static boolean trySend(
94             PendingIntent pendingIntent, @Nullable ActivityOptions activityOptions) {
95         try {
96             send(pendingIntent, activityOptions);
97             return true;
98         } catch (PendingIntent.CanceledException ex) {
99             Log.e(
100                     TAG,
101                     "Couldn't send PendingIntent: "
102                             + pendingIntent
103                             + " with ActivityOptions:"
104                             + activityOptions,
105                     ex);
106             return false;
107         }
108     }
109 
110     @Nullable
createActivityOptions( PendingIntent pendingIntent, @Nullable Integer launchTaskId)111     private static ActivityOptions createActivityOptions(
112             PendingIntent pendingIntent, @Nullable Integer launchTaskId) {
113         if (!pendingIntent.isActivity()) {
114             return null;
115         }
116         if (launchTaskId == null && !SdkLevel.isAtLeastU()) {
117             return null;
118         }
119         ActivityOptions activityOptions = ActivityOptions.makeBasic();
120         if (launchTaskId != null) {
121             activityOptions.setLaunchTaskId(launchTaskId);
122         }
123         if (SdkLevel.isAtLeastU()) {
124             setBackgroundActivityStartModeAllowed(activityOptions);
125         }
126         return activityOptions;
127     }
128 
129     @RequiresApi(UPSIDE_DOWN_CAKE)
setBackgroundActivityStartModeAllowed(ActivityOptions activityOptions)130     private static void setBackgroundActivityStartModeAllowed(ActivityOptions activityOptions) {
131         activityOptions.setPendingIntentBackgroundActivityStartMode(
132                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
133     }
134 }
135