| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License 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.google.ipc.invalidation.external.client.contrib; |
| |
| import com.google.ipc.invalidation.external.client.SystemResources.Logger; |
| import com.google.ipc.invalidation.external.client.android.service.AndroidLogger; |
| import com.google.ipc.invalidation.external.client.contrib.AndroidListener.AlarmReceiver; |
| import com.google.ipc.invalidation.external.client.types.ObjectId; |
| import com.google.ipc.invalidation.ticl.android2.AndroidClock; |
| import com.google.ipc.invalidation.ticl.android2.AndroidTiclManifest; |
| import com.google.ipc.invalidation.ticl.android2.channel.AndroidChannelConstants.AuthTokenConstants; |
| import com.google.ipc.invalidation.ticl.proto.AndroidListenerProtocol.RegistrationCommand; |
| import com.google.ipc.invalidation.ticl.proto.AndroidListenerProtocol.StartCommand; |
| import com.google.ipc.invalidation.util.Bytes; |
| import com.google.ipc.invalidation.util.ProtoWrapper.ValidationException; |
| |
| import android.app.AlarmManager; |
| import android.app.PendingIntent; |
| import android.app.PendingIntent.CanceledException; |
| import android.content.Context; |
| import android.content.Intent; |
| |
| |
| /** |
| * Static helper class supporting construction and decoding of intents issued and handled by the |
| * {@link AndroidListener}. |
| * |
| */ |
| class AndroidListenerIntents { |
| |
| /** The logger. */ |
| private static final Logger logger = AndroidLogger.forPrefix(""); |
| |
| /** Key of Intent byte[] holding a {@link RegistrationCommand} protocol buffer. */ |
| static final String EXTRA_REGISTRATION = |
| "com.google.ipc.invalidation.android_listener.REGISTRATION"; |
| |
| /** Key of Intent byte[] holding a {@link StartCommand} protocol buffer. */ |
| static final String EXTRA_START = |
| "com.google.ipc.invalidation.android_listener.START"; |
| |
| /** Key of Intent extra indicating that the client should stop. */ |
| static final String EXTRA_STOP = |
| "com.google.ipc.invalidation.android_listener.STOP"; |
| |
| /** Key of Intent extra holding a byte[] that is ack handle data. */ |
| static final String EXTRA_ACK = |
| "com.google.ipc.invalidation.android_listener.ACK"; |
| |
| /** |
| * Issues the given {@code intent} to the TICL service class registered in the {@code context}. |
| */ |
| static void issueTiclIntent(Context context, Intent intent) { |
| context.startService(intent.setClassName(context, |
| new AndroidTiclManifest(context).getTiclServiceClass())); |
| } |
| |
| /** |
| * Issues the given {@code intent} to the {@link AndroidListener} class registered in the |
| * {@code context}. |
| */ |
| static void issueAndroidListenerIntent(Context context, Intent intent) { |
| context.startService(setAndroidListenerClass(context, intent)); |
| } |
| |
| /** |
| * Returns the ack handle from the given intent if it has the appropriate extra. Otherwise, |
| * returns {@code null}. |
| */ |
| static byte[] findAckHandle(Intent intent) { |
| return intent.getByteArrayExtra(EXTRA_ACK); |
| } |
| |
| /** |
| * Returns {@link RegistrationCommand} extra from the given intent or null if no valid |
| * registration command exists. |
| */ |
| static RegistrationCommand findRegistrationCommand(Intent intent) { |
| // Check that the extra exists. |
| byte[] data = intent.getByteArrayExtra(EXTRA_REGISTRATION); |
| if (null == data) { |
| return null; |
| } |
| |
| // Attempt to parse the extra. |
| try { |
| return RegistrationCommand.parseFrom(data); |
| } catch (ValidationException exception) { |
| logger.warning("Received invalid proto: %s", exception); |
| return null; |
| } |
| } |
| |
| /** |
| * Returns {@link StartCommand} extra from the given intent or null if no valid start command |
| * exists. |
| */ |
| static StartCommand findStartCommand(Intent intent) { |
| // Check that the extra exists. |
| byte[] data = intent.getByteArrayExtra(EXTRA_START); |
| if (null == data) { |
| return null; |
| } |
| |
| // Attempt to parse the extra. |
| try { |
| return StartCommand.parseFrom(data); |
| } catch (ValidationException exception) { |
| logger.warning("Received invalid proto: %s", exception); |
| return null; |
| } |
| } |
| |
| /** Returns {@code true} if the intent has the 'stop' extra. */ |
| static boolean isStopIntent(Intent intent) { |
| return intent.hasExtra(EXTRA_STOP); |
| } |
| |
| /** Issues a registration retry with delay. */ |
| static void issueDelayedRegistrationIntent(Context context, AndroidClock clock, |
| Bytes clientId, ObjectId objectId, boolean isRegister, int delayMs, int requestCode) { |
| RegistrationCommand command = isRegister ? |
| AndroidListenerProtos.newDelayedRegisterCommand(clientId, objectId) : |
| AndroidListenerProtos.newDelayedUnregisterCommand(clientId, objectId); |
| Intent intent = new Intent() |
| .putExtra(EXTRA_REGISTRATION, command.toByteArray()) |
| .setClass(context, AlarmReceiver.class); |
| |
| // Create a pending intent that will cause the AlarmManager to fire the above intent. |
| PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, |
| PendingIntent.FLAG_ONE_SHOT); |
| |
| // Schedule the pending intent after the appropriate delay. |
| AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); |
| long executeMs = clock.nowMs() + delayMs; |
| alarmManager.set(AlarmManager.RTC, executeMs, pendingIntent); |
| } |
| |
| /** Creates a 'start-client' intent. */ |
| static Intent createStartIntent(Context context, int clientType, Bytes clientName, |
| boolean allowSuppression) { |
| Intent intent = new Intent(); |
| // Create proto for the start command. |
| StartCommand command = |
| AndroidListenerProtos.newStartCommand(clientType, clientName, allowSuppression); |
| intent.putExtra(EXTRA_START, command.toByteArray()); |
| return setAndroidListenerClass(context, intent); |
| } |
| |
| /** Creates a 'stop-client' intent. */ |
| static Intent createStopIntent(Context context) { |
| // Stop command just has the extra (its content doesn't matter). |
| Intent intent = new Intent(); |
| intent.putExtra(EXTRA_STOP, true); |
| return setAndroidListenerClass(context, intent); |
| } |
| |
| /** Create an ack intent. */ |
| static Intent createAckIntent(Context context, byte[] ackHandle) { |
| // Ack intent has an extra containing the ack handle data. |
| Intent intent = new Intent(); |
| intent.putExtra(EXTRA_ACK, ackHandle); |
| return setAndroidListenerClass(context, intent); |
| } |
| |
| /** Constructs an intent with {@link RegistrationCommand} proto. */ |
| static Intent createRegistrationIntent(Context context, Bytes clientId, |
| Iterable<ObjectId> objectIds, boolean isRegister) { |
| // Registration intent has an extra containing the RegistrationCommand proto. |
| Intent intent = new Intent(); |
| RegistrationCommand command = |
| AndroidListenerProtos.newRegistrationCommand(clientId, objectIds, isRegister); |
| intent.putExtra(EXTRA_REGISTRATION, command.toByteArray()); |
| return setAndroidListenerClass(context, intent); |
| } |
| |
| /** Sets the appropriate class for {@link AndroidListener} service intents. */ |
| static Intent setAndroidListenerClass(Context context, Intent intent) { |
| String simpleListenerClass = new AndroidTiclManifest(context).getListenerServiceClass(); |
| return intent.setClassName(context, simpleListenerClass); |
| } |
| |
| /** Returns {@code true} iff the given intent is an authorization token request. */ |
| static boolean isAuthTokenRequest(Intent intent) { |
| return AuthTokenConstants.ACTION_REQUEST_AUTH_TOKEN.equals(intent.getAction()); |
| } |
| |
| /** |
| * Given an authorization token request intent and authorization information ({@code authToken} |
| * and {@code authType}) issues a response. |
| */ |
| static void issueAuthTokenResponse(Context context, PendingIntent pendingIntent, String authToken, |
| String authType) { |
| Intent responseIntent = new Intent() |
| .putExtra(AuthTokenConstants.EXTRA_AUTH_TOKEN, authToken) |
| .putExtra(AuthTokenConstants.EXTRA_AUTH_TOKEN_TYPE, authType); |
| try { |
| pendingIntent.send(context, 0, responseIntent); |
| } catch (CanceledException exception) { |
| logger.warning("Canceled auth request: %s", exception); |
| } |
| } |
| |
| // Prevent instantiation. |
| private AndroidListenerIntents() { |
| } |
| } |