Home | History | Annotate | Download | only in intent
      1 /*
      2  * Copyright (C) 2019 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.server.wm.intent;
     18 
     19 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
     20 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
     21 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
     22 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
     23 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
     24 import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
     25 import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
     26 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
     27 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
     28 import static android.server.wm.intent.LaunchSequence.intent;
     29 import static android.server.wm.intent.LaunchSequence.intentForResult;
     30 import static android.server.wm.intent.Persistence.flag;
     31 
     32 import android.content.Intent;
     33 import android.server.wm.intent.Activities.RegularActivity;
     34 import android.server.wm.intent.Persistence.IntentFlag;
     35 import android.server.wm.intent.Persistence.LaunchIntent;
     36 
     37 import com.google.common.collect.Lists;
     38 
     39 import java.util.HashMap;
     40 import java.util.List;
     41 import java.util.Map;
     42 
     43 /**
     44  * Contains all information to create and reuse intent test launches.
     45  * It enumerates all the flags so they can easily be used.
     46  *
     47  * It also stores commonly used {@link LaunchSequence} objects for reuse.
     48  */
     49 public class Cases {
     50 
     51     public static final IntentFlag CLEAR_TASK = flag(FLAG_ACTIVITY_CLEAR_TASK,
     52             "FLAG_ACTIVITY_CLEAR_TASK");
     53     public static final IntentFlag CLEAR_TOP = flag(FLAG_ACTIVITY_CLEAR_TOP,
     54             "FLAG_ACTIVITY_CLEAR_TOP");
     55     private static final IntentFlag SINGLE_TOP = flag(FLAG_ACTIVITY_SINGLE_TOP,
     56             "FLAG_ACTIVITY_SINGLE_TOP");
     57     public static final IntentFlag NEW_TASK = flag(FLAG_ACTIVITY_NEW_TASK,
     58             "FLAG_ACTIVITY_NEW_TASK");
     59     public static final IntentFlag NEW_DOCUMENT = flag(FLAG_ACTIVITY_NEW_DOCUMENT,
     60             "FLAG_ACTIVITY_NEW_DOCUMENT");
     61     private static final IntentFlag MULTIPLE_TASK = flag(FLAG_ACTIVITY_MULTIPLE_TASK,
     62             "FLAG_ACTIVITY_MULTIPLE_TASK");
     63     public static final IntentFlag RESET_TASK_IF_NEEDED = flag(
     64             FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,
     65             "FLAG_ACTIVITY_RESET_TASK_IF_NEEDED");
     66     public static final IntentFlag PREVIOUS_IS_TOP = flag(FLAG_ACTIVITY_PREVIOUS_IS_TOP,
     67             "FLAG_ACTIVITY_PREVIOUS_IS_TOP");
     68     public static final IntentFlag REORDER_TO_FRONT = flag(FLAG_ACTIVITY_REORDER_TO_FRONT,
     69             "FLAG_ACTIVITY_REORDER_TO_FRONT");
     70 
     71     // Flag only used for parsing intents that contain no flags.
     72     private static final IntentFlag NONE = flag(0, "");
     73 
     74     public final List<IntentFlag> flags = Lists.newArrayList(
     75             CLEAR_TASK,
     76             CLEAR_TOP,
     77             SINGLE_TOP,
     78             NEW_TASK,
     79             NEW_DOCUMENT,
     80             MULTIPLE_TASK,
     81             RESET_TASK_IF_NEEDED,
     82             PREVIOUS_IS_TOP,
     83             REORDER_TO_FRONT
     84     );
     85 
     86     // Definition of intents used across multiple test cases.
     87     private final LaunchIntent mRegularIntent = intent(RegularActivity.class);
     88     private final LaunchIntent mSingleTopIntent = intent(Activities.SingleTopActivity.class);
     89     private final LaunchIntent mAff1Intent = intent(Activities.TaskAffinity1Activity.class);
     90     private final LaunchIntent mSecondAff1Intent = intent(Activities.TaskAffinity1Activity2.class);
     91     private final LaunchIntent mSingleInstanceIntent = intent(
     92             Activities.SingleInstanceActivity.class);
     93     private final LaunchIntent mSingleTaskIntent = intent(Activities.SingleTaskActivity.class);
     94     private final LaunchIntent mRegularForResultIntent = intentForResult(RegularActivity.class);
     95 
     96     // LaunchSequences used across multiple test cases.
     97     private final LaunchSequence mRegularSequence = LaunchSequence.create(mRegularIntent);
     98 
     99     // To show that the most recent task get's resumed by new task
    100     private final LaunchSequence mTwoAffinitiesSequence =
    101             LaunchSequence.create(mAff1Intent)
    102                     .append(mRegularIntent)
    103                     .append(mAff1Intent.withFlags(NEW_TASK, MULTIPLE_TASK));
    104 
    105     // Used to show that the task affinity is determined by the activity that started it,
    106     // Not the affinity of the  current root activity.
    107     private final LaunchSequence mRearrangedRootSequence = LaunchSequence.create(mRegularIntent)
    108             .append(mAff1Intent.withFlags(NEW_TASK))
    109             .append(mRegularIntent)
    110             .append(mAff1Intent.withFlags(REORDER_TO_FRONT));
    111 
    112 
    113     private final LaunchSequence mSingleInstanceActivitySequence =
    114             LaunchSequence.create(mSingleInstanceIntent);
    115 
    116     private final LaunchSequence mSingleTaskActivitySequence = LaunchSequence.create(
    117             mSingleTaskIntent);
    118 
    119     public List<LaunchSequence> newTaskCases() {
    120         LaunchIntent aff1NewTask = mAff1Intent.withFlags(NEW_TASK);
    121 
    122         return Lists.newArrayList(
    123                 // 1. Single instance will start a new task even without new task set
    124                 mRegularSequence.act(mSingleInstanceIntent),
    125                 // 2. With new task it will still end up in a new task
    126                 mRegularSequence.act(mSingleInstanceIntent.withFlags(NEW_TASK)),
    127                 // 3. Starting an activity with a different affinity without new task will put it in
    128                 // the existing task
    129                 mRegularSequence.act(mAff1Intent),
    130                 // 4. Starting a task with a different affinity and new task will start a new task
    131                 mRegularSequence.act(aff1NewTask),
    132                 // 5. Starting the intent with new task will not add a new activity to the task
    133                 mRegularSequence.act(mRegularIntent.withFlags(NEW_TASK)),
    134                 // 6. A different activity with the same affinity will start a new activity in
    135                 // the sam task
    136                 mRegularSequence.act(mSingleTopIntent.withFlags(NEW_TASK)),
    137                 // 7. To show that the most recent task get's resumed by new task, this can't
    138                 // be observed without differences in the same activity / task
    139                 mTwoAffinitiesSequence.act(aff1NewTask),
    140                 // 8. To show that new task respects the root as a single even
    141                 // if it is not at the bottom
    142                 mRearrangedRootSequence.act(mAff1Intent.withFlags(NEW_TASK)),
    143                 // 9. To show that new task with non root does start a new activity.
    144                 mRearrangedRootSequence.act(mSecondAff1Intent.withFlags(NEW_TASK)),
    145                 // 10. Multiple task will allow starting activities of the same affinity in
    146                 // different tasks
    147                 mRegularSequence.act(mRegularIntent.withFlags(NEW_TASK, MULTIPLE_TASK)),
    148                 // 11. Single instance will not start a new task even with multiple task on
    149                 mSingleInstanceActivitySequence.act(
    150                         mSingleInstanceIntent.withFlags(NEW_TASK, MULTIPLE_TASK)),
    151                 // 12. The same should happen for single task.
    152                 mSingleTaskActivitySequence.act(
    153                         mSingleTaskIntent.withFlags(NEW_TASK, MULTIPLE_TASK)),
    154                 // 13. This starts a regular in a new task
    155                 mSingleInstanceActivitySequence.act(mRegularIntent),
    156                 // 14. This adds regular in the same task
    157                 mSingleTaskActivitySequence.act(mRegularIntent),
    158                 // 15. Starting the activity for result keeps it in the same task
    159                 mSingleInstanceActivitySequence.act(mRegularForResultIntent),
    160                 // 16. Restarts the previous task with regular activity.
    161                 mRegularSequence.append(mSingleInstanceIntent).act(mRegularIntent)
    162         );
    163     }
    164 
    165     /**
    166      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT }test cases are the same as
    167      * {@link Cases#newTaskCases()}, we should check them for differences.
    168      */
    169     public List<LaunchSequence> newDocumentCases() {
    170         LaunchIntent aff1NewDocument = mAff1Intent.withFlags(NEW_DOCUMENT);
    171 
    172         return Lists.newArrayList(
    173                 // 1. Single instance will start a new task even without new task set
    174                 mRegularSequence.act(mSingleInstanceIntent),
    175                 // 2. With new task it will still end up in a new task
    176                 mRegularSequence.act(mSingleInstanceIntent.withFlags(NEW_DOCUMENT)),
    177                 // 3. Starting an activity with a different affinity without new task will put it
    178                 // in the existing task
    179                 mRegularSequence.act(mAff1Intent),
    180                 // 4. With new document it will start it's own task
    181                 mRegularSequence.act(aff1NewDocument),
    182                 // 5. Starting the intent with new task will not add a new activity to the task
    183                 mRegularSequence.act(mRegularIntent.withFlags(NEW_DOCUMENT)),
    184                 // 6. Unlike the case with NEW_TASK, with new Document
    185                 mRegularSequence.act(mSingleTopIntent.withFlags(NEW_DOCUMENT)),
    186                 // 7. To show that the most recent task get's resumed by new task
    187                 mTwoAffinitiesSequence.act(aff1NewDocument),
    188                 // 8. To show that new task respects the root as a single
    189                 mRearrangedRootSequence.act(mAff1Intent.withFlags(NEW_DOCUMENT)),
    190                 // 9. New document starts a third task here, because there was no task for the
    191                 // document yet
    192                 mRearrangedRootSequence.act(mSecondAff1Intent.withFlags(NEW_DOCUMENT)),
    193                 // 10. Multiple task wil allow starting activities of the same affinity in different
    194                 // tasks
    195                 mRegularSequence.act(mRegularIntent.withFlags(NEW_DOCUMENT, MULTIPLE_TASK)),
    196                 // 11. Single instance will not start a new task even with multiple task on
    197                 mSingleInstanceActivitySequence
    198                         .act(mSingleInstanceIntent.withFlags(NEW_DOCUMENT, MULTIPLE_TASK)),
    199                 // 12. The same should happen for single task.
    200                 mSingleTaskActivitySequence.act(
    201                         mSingleTaskIntent.withFlags(NEW_DOCUMENT, MULTIPLE_TASK))
    202         );
    203     }
    204 
    205     public List<LaunchSequence> clearCases() {
    206         LaunchSequence doubleRegularActivity = mRegularSequence.append(mRegularIntent);
    207 
    208         return Lists.newArrayList(
    209                 // 1. This will clear the bottom and end up with just one activity
    210                 mRegularSequence.act(mRegularIntent.withFlags(CLEAR_TOP)),
    211                 // 2. This will result in still two regulars
    212                 doubleRegularActivity.act(mRegularIntent.withFlags(CLEAR_TOP)),
    213                 // 3. This will result in a single regular it clears the top regular
    214                 // activity and then fails to start a new regular activity because it is already
    215                 // at the root of the task
    216                 doubleRegularActivity.act(mRegularIntent.withFlags(CLEAR_TOP, NEW_TASK)),
    217                 // 3. This will also result in two regulars, showing the first difference between
    218                 // new document and new task.
    219                 doubleRegularActivity.act(mRegularIntent.withFlags(CLEAR_TOP, NEW_DOCUMENT)),
    220                 // 4. This is here to show that previous is top has no effect on clear top or single
    221                 // top
    222                 doubleRegularActivity.act(mRegularIntent.withFlags(CLEAR_TOP, PREVIOUS_IS_TOP)),
    223                 // 5. Clear top finds the same activity in the task and clears from there
    224                 mRegularSequence.append(mAff1Intent).append(mRegularIntent).act(
    225                         mAff1Intent.withFlags(CLEAR_TOP))
    226         );
    227     }
    228 
    229     /**
    230      * Tests for {@link android.app.Activity#startActivityForResult(Intent, int)}
    231      */
    232     //TODO: If b/122968776 is fixed these tests need to be updated
    233     public List<LaunchSequence> forResultCases() {
    234         LaunchIntent singleTopForResult = intentForResult(Activities.SingleTopActivity.class);
    235 
    236         return Lists.newArrayList(
    237                 // 1. Start just a single regular activity
    238                 mRegularSequence.act(mRegularIntent.withFlags(SINGLE_TOP)),
    239                 // 2. For result will start a second activity
    240                 mRegularSequence.act(mRegularForResultIntent.withFlags(SINGLE_TOP)),
    241                 // 3. The same but for SINGLE_TOP as a launch mode
    242                 LaunchSequence.create(mSingleTopIntent).act(mSingleTopIntent),
    243                 // 4. Launch mode SINGLE_TOP when launched for result also starts a second activity.
    244                 LaunchSequence.create(mSingleTopIntent).act(singleTopForResult),
    245                 // 5. CLEAR_TOP results in a single regular activity
    246                 mRegularSequence.act(mRegularIntent.withFlags(CLEAR_TOP)),
    247                 // 6. Clear will still kill the for result
    248                 mRegularSequence.act(mRegularForResultIntent.withFlags(CLEAR_TOP)),
    249                 // 7. An activity started for result can go to a different task
    250                 mRegularSequence.act(mRegularForResultIntent.withFlags(NEW_TASK, MULTIPLE_TASK)),
    251                 // 8. Reorder to front with for result
    252                 mRegularSequence.append(mAff1Intent).act(
    253                         mRegularForResultIntent.withFlags(REORDER_TO_FRONT)),
    254                 // 9. Reorder can move an activity above one that it started for result
    255                 mRegularSequence.append(intentForResult(Activities.TaskAffinity1Activity.class))
    256                         .act(mRegularIntent.withFlags(REORDER_TO_FRONT))
    257         );
    258     }
    259 
    260     /**
    261      * Reset task if needed will trigger when it is delivered with new task set
    262      * and there are activities in the task that have a different affinity.
    263      *
    264      * @return the test cases
    265      */
    266     //TODO: If b/122324373 is fixed these test need to be updated.
    267     public List<LaunchSequence> resetTaskIfNeeded() {
    268         // If a task with a different affinity like this get's reset
    269         // it will create another task in the same stack with the now orphaned activity.
    270         LaunchIntent resetRegularTask = mRegularIntent.withFlags(NEW_TASK,
    271                 RESET_TASK_IF_NEEDED);
    272         LaunchSequence resetIfNeeded = mRegularSequence.append(mAff1Intent)
    273                 .act(resetRegularTask);
    274 
    275         // If you try to reset a task with an activity that was started for result
    276         // it will not move task.
    277         LaunchSequence resetWontMoveResult = mRegularSequence.append(
    278                 intentForResult(Activities.TaskAffinity1Activity.class))
    279                 .act(resetRegularTask);
    280 
    281         // Reset will not move activities with to a task with that affinity,
    282         // instead it will always create a new task in that stack.
    283         LaunchSequence resetToExistingTask2 = mRegularSequence
    284                 .append(mAff1Intent)
    285                 .append(mAff1Intent.withFlags(NEW_TASK))
    286                 .act(resetRegularTask);
    287 
    288         // If a reset occurs the activities that move retain the same order
    289         // in the new task as they had in the old task.
    290         LaunchSequence resetOrdering = mRegularSequence
    291                 .append(mAff1Intent)
    292                 .append(mSecondAff1Intent)
    293                 .act(resetRegularTask);
    294 
    295         return Lists.newArrayList(resetIfNeeded, resetWontMoveResult, resetToExistingTask2,
    296                 resetOrdering);
    297     }
    298 
    299     /**
    300      * The human readable flags in the JSON files need to be converted back to the corresponding
    301      * IntentFlag object when reading the file. This creates a map from the flags to their
    302      * corresponding object.
    303      *
    304      * @return lookup table for the parsing of intent flags in the json files.
    305      */
    306     public Map<String, IntentFlag> createFlagParsingTable() {
    307         HashMap<String, IntentFlag> flags = new HashMap<>();
    308         for (IntentFlag flag : this.flags) {
    309             flags.put(flag.name, flag);
    310         }
    311 
    312         flags.put(NONE.getName(), NONE);
    313         return flags;
    314     }
    315 }
    316