Skip to content

Commit

Permalink
Fix(pinpoint) 2762: Resolve Android 12 notification trampolining rest…
Browse files Browse the repository at this point in the history
…rictions and ADM Push Notifications (#2925)

* Resolve Android 12 notification trampolining restrictions

* Update pinpoint notification test

* set correct target class for ADM

* remove line from test

Co-authored-by: Divyesh Chitroda <div5yesh@gmail.com>
  • Loading branch information
tylerjroach and div5yesh authored Jun 9, 2022
1 parent f34d1e7 commit a544544
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 53 deletions.
7 changes: 7 additions & 0 deletions aws-android-sdk-pinpoint/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amazonaws.services.pinpoint">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<activity
android:name="com.amazonaws.mobileconnectors.pinpoint.targeting.notification.PinpointNotificationActivity"
android:theme="@style/PinpointNotificationActivityTheme"
android:launchMode="singleInstance"
android:exported="false" />
</application>
</manifest>

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import com.amazonaws.mobileconnectors.pinpoint.targeting.TargetingClient;
import com.amazonaws.mobileconnectors.pinpoint.targeting.notification.DeviceTokenRegisteredHandler;
import com.amazonaws.mobileconnectors.pinpoint.targeting.notification.NotificationClient;
import com.amazonaws.mobileconnectors.pinpoint.targeting.notification.PinpointNotificationReceiver;
import com.amazonaws.mobileconnectors.pinpoint.targeting.notification.PinpointNotificationActivity;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.pinpoint.AmazonPinpointClient;
import com.amazonaws.services.pinpoint.model.ChannelType;
Expand Down Expand Up @@ -87,7 +87,7 @@ public PinpointManager(final PinpointConfiguration config) {
this.pinpointContext = new PinpointContext(analyticsServiceClient, targetingServiceClient, appContext, appId, SDK_INFO, config);
this.notificationClient = NotificationClient.createClient(this.pinpointContext, channelType);
this.pinpointContext.setNotificationClient(this.notificationClient);
PinpointNotificationReceiver.setNotificationClient(this.notificationClient);
PinpointNotificationActivity.setNotificationClient(this.notificationClient);

if (config.getEnableEvents()) {
this.analyticsClient = new AnalyticsClient(this.pinpointContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ protected PendingIntent createOpenAppPendingIntent(final Bundle pushBundle, fina
flags |= PendingIntent.FLAG_IMMUTABLE;
}

contentIntent = PendingIntent.getService(pinpointContext.getApplicationContext(), requestId,
contentIntent = PendingIntent.getActivity(pinpointContext.getApplicationContext(), requestId,
this.notificationIntent(pushBundle, eventSourceId, requestId, NotificationClient.ADM_INTENT_ACTION,
targetClass), flags);
PinpointNotificationActivity.setNotificationClient(this);
}
return contentIntent;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ protected PendingIntent createOpenAppPendingIntent(Bundle pushBundle, Class<?> t
flags |= PendingIntent.FLAG_IMMUTABLE;
}

contentIntent = PendingIntent.getBroadcast(pinpointContext.getApplicationContext(), requestId,
contentIntent = PendingIntent.getActivity(pinpointContext.getApplicationContext(), requestId,
this.notificationIntent(pushBundle, eventSourceId, requestId, NotificationClient.BAIDU_INTENT_ACTION,
targetClass), flags);
PinpointNotificationReceiver.setNotificationClient(this);
PinpointNotificationActivity.setNotificationClient(this);
}
return contentIntent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,11 @@ protected PendingIntent createOpenAppPendingIntent(final Bundle pushBundle, fina
this.notificationIntent(pushBundle, eventSourceId, requestId, NotificationClient.GCM_INTENT_ACTION,
targetClass), flags);
} else {
contentIntent = PendingIntent.getBroadcast(pinpointContext.getApplicationContext(), requestId,
contentIntent = PendingIntent.getActivity(pinpointContext.getApplicationContext(), requestId,
this.notificationIntent(pushBundle, eventSourceId, requestId, NotificationClient.FCM_INTENT_ACTION,
targetClass), flags);
PinpointNotificationReceiver.setNotificationClient(this);
PinpointNotificationActivity.setNotificationClient(this);
}
return contentIntent;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public NotificationDetails build() {
}

notificationDetails.setFrom(from);
notificationDetails.setTargetClass(PinpointNotificationReceiver.class);
notificationDetails.setTargetClass(PinpointNotificationActivity.class);
notificationDetails.setIntentAction(intentAction);
notificationDetails.setNotificationChannelId(notificationChannelId);
}
Expand All @@ -257,7 +257,7 @@ public NotificationDetails build() {
}
notificationDetails.setBundle(data);
}
notificationDetails.setTargetClass(serviceClass);
notificationDetails.setTargetClass(PinpointNotificationActivity.class);
notificationDetails.setIntentAction(intentAction);
}

Expand All @@ -277,7 +277,7 @@ public NotificationDetails build() {
notificationDetails.setFrom(localFrom);
notificationDetails.setBundle(localBundle);
}
notificationDetails.setTargetClass(PinpointNotificationReceiver.class);
notificationDetails.setTargetClass(PinpointNotificationActivity.class);
notificationDetails.setIntentAction(intentAction);
} catch (JSONException e) {
log.error("Unable to parse JSON message: " + e, e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.amazonaws.mobileconnectors.pinpoint.targeting.notification;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;

/**
* The Amazon Pinpoint push notification tracking/routing activity.
* This class is an Activity instead of BroadcastReceiver due to Android 12 restrictions around
* notification trampolining.
*/
public class PinpointNotificationActivity extends Activity {
private static String TAG = PinpointNotificationActivity.class.getSimpleName();

private static volatile NotificationClient notificationClient = null;

public static void setNotificationClient(NotificationClient notificationClient) {
PinpointNotificationActivity.notificationClient = notificationClient;
}

public static void setNotificationClient(NotificationClientBase notificationClientBase) {
PinpointNotificationActivity.notificationClient = new NotificationClient(notificationClientBase);
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (notificationClient != null) {
EventSourceType eventSourceType = EventSourceType.getEventSourceType(extras);
notificationClient.handleNotificationOpen(eventSourceType.getAttributeParser().parseAttributes(extras), extras);
} else {
final PackageManager pm = getPackageManager();
final Intent launchIntent = pm.getLaunchIntentForPackage(intent.getPackage());
launchIntent.putExtras(extras);
startActivity(launchIntent);
}

finish();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2016-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
Expand All @@ -15,41 +15,19 @@

package com.amazonaws.mobileconnectors.pinpoint.targeting.notification;

import java.util.HashMap;
import java.util.Map;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.util.Log;

/**
* The Amazon Pinpoint push notification receiver.
* @deprecated This class is no longer functional and will soon be removed. At one point,
* we required adding this receiver to the app manifest. That code block should be removed.
*/
@Deprecated
public class PinpointNotificationReceiver extends BroadcastReceiver {
private static String TAG = PinpointNotificationReceiver.class.getSimpleName();

private static volatile NotificationClient notificationClient = null;

public static void setNotificationClient(NotificationClient notificationClient) {
PinpointNotificationReceiver.notificationClient = notificationClient;
}

public static void setNotificationClient(NotificationClientBase notificationClientBase) {
PinpointNotificationReceiver.notificationClient = new NotificationClient(notificationClientBase);
}

@Override
public void onReceive(Context context, Intent intent) {
if (notificationClient != null) {
EventSourceType eventSourceType = EventSourceType.getEventSourceType(intent.getExtras());
notificationClient.handleNotificationOpen(eventSourceType.getAttributeParser().parseAttributes(intent.getExtras()),
intent.getExtras());
} else {
final PackageManager pm = context.getPackageManager();
final Intent launchIntent = pm.getLaunchIntentForPackage(intent.getPackage());
launchIntent.putExtras(intent.getExtras());
context.startActivity(launchIntent);
}
// do nothing
}
}
14 changes: 14 additions & 0 deletions aws-android-sdk-pinpoint/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<!--This theme ensures the Headless PinpointNotificationActivity is completely hidden-->
<style name="PinpointNotificationActivityTheme" parent="android:Theme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,46 @@

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.android.controller.ActivityController;

@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, sdk=23)
public class PinpointNotificationReceiverTests {
public class PinpointNotificationActivityTests {

private NotificationClient notificationClient;

private PinpointNotificationReceiver receiver;

private Intent intent;

private Context context;
private ActivityController<PinpointNotificationActivity> activityController;

@Before
public void setup() {
notificationClient = Mockito.mock(NotificationClient.class);
intent = Mockito.mock(Intent.class);
receiver = new PinpointNotificationReceiver();
PinpointNotificationReceiver.setNotificationClient(notificationClient);
context = Mockito.mock(Context.class);
activityController = Robolectric.buildActivity(PinpointNotificationActivity.class);
PinpointNotificationActivity.setNotificationClient(notificationClient);
}

@After
public void tearDown() {
activityController.destroy();
PinpointNotificationActivity.setNotificationClient((NotificationClient) null);
}

@Test
public void testReferenceToNotificationClient() {
System.gc();
receiver.onReceive(context, intent);
verifyZeroInteractions(notificationClient);

activityController.create();

verify(notificationClient, times(1)).handleNotificationOpen(Mockito.anyMap(),
Mockito.any(Bundle.class));
}
Expand Down

0 comments on commit a544544

Please sign in to comment.