package com.zegoggles.smssync.service; import android.annotation.TargetApi; import android.content.Intent; import android.os.Build; import android.os.PowerManager; import android.provider.Telephony; import android.util.Log; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.BinaryTempFileBody; import com.squareup.otto.Produce; import com.squareup.otto.Subscribe; import com.zegoggles.smssync.App; import com.zegoggles.smssync.R; import com.zegoggles.smssync.auth.OAuth2Client; import com.zegoggles.smssync.auth.TokenRefresher; import com.zegoggles.smssync.contacts.ContactAccessor; import com.zegoggles.smssync.mail.MessageConverter; import com.zegoggles.smssync.mail.PersonLookup; import com.zegoggles.smssync.preferences.AuthPreferences; import com.zegoggles.smssync.service.exception.SmsProviderNotWritableException; import com.zegoggles.smssync.service.state.RestoreState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FilenameFilter; import static com.zegoggles.smssync.App.LOCAL_LOGV; import static com.zegoggles.smssync.App.TAG; import static com.zegoggles.smssync.mail.DataType.CALLLOG; import static com.zegoggles.smssync.mail.DataType.SMS; import static com.zegoggles.smssync.service.state.SmsSyncState.ERROR; public class SmsRestoreService extends ServiceBase { private static final int RESTORE_ID = 2; @NotNull private RestoreState mState = new RestoreState(); @Nullable private static SmsRestoreService service; @Override @NotNull public RestoreState getState() { return mState; } @Override public void onCreate() { super.onCreate(); asyncClearCache(); BinaryTempFileBody.setTempDirectory(getCacheDir()); service = this; } @Override public void onDestroy() { super.onDestroy(); if (LOCAL_LOGV) Log.v(TAG, "SmsRestoreService#onDestroy(state"+getState()+")"); service = null; } /** * Android KitKat and above require SMS Backup+ to be the default SMS application in order to * write to the SMS Provider. */ @TargetApi(Build.VERSION_CODES.KITKAT) private boolean canWriteToSmsProvider() { return Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || getPackageName().equals(Telephony.Sms.getDefaultSmsPackage(this)); } @Override protected void handleIntent(final Intent intent) { if (isWorking()) return; try { final boolean restoreCallLog = CALLLOG.isRestoreEnabled(service); final boolean restoreSms = SMS.isRestoreEnabled(service); if (restoreSms && !canWriteToSmsProvider()) { postError(new SmsProviderNotWritableException()); return; } MessageConverter converter = new MessageConverter(service, getPreferences(), getAuthPreferences().getUserEmail(), new PersonLookup(getContentResolver()), ContactAccessor.Get.instance() ); RestoreConfig config = new RestoreConfig( getBackupImapStore(), 0, restoreSms, restoreCallLog, getPreferences().isRestoreStarredOnly(), getPreferences().getMaxItemsPerRestore(), 0 ); final AuthPreferences authPreferences = new AuthPreferences(this); new RestoreTask(this, converter, getContentResolver(), new TokenRefresher(service, new OAuth2Client(authPreferences.getOAuth2ClientId()), authPreferences)).execute(config); } catch (MessagingException e) { postError(e); } } private void postError(Exception exception) { App.bus.post(mState.transition(ERROR, exception)); } private void asyncClearCache() { new Thread("clearCache") { @Override public void run() { clearCache(); } }.start(); } public synchronized void clearCache() { File tmp = getCacheDir(); if (tmp == null) return; // not sure why this would return null Log.d(TAG, "clearing cache in " + tmp); for (File f : tmp.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.startsWith("body"); } })) { if (LOCAL_LOGV) Log.v(TAG, "deleting " + f); if (!f.delete()) Log.w(TAG, "error deleting " + f); } } @Subscribe public void restoreStateChanged(final RestoreState state) { mState = state; if (mState.isInitialState()) return; if (mState.isRunning()) { notification = createNotification(R.string.status_restore) .setContentTitle(getString(R.string.status_restore)) .setContentText(state.getNotificationLabel(getResources())) .setContentIntent(getPendingIntent()) .getNotification(); startForeground(RESTORE_ID, notification); } else { Log.d(TAG, "stopping service, state"+mState); stopForeground(true); stopSelf(); } } @Produce public RestoreState produceLastState() { return mState; } @Override protected int wakeLockType() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // hold a full wake lock when restoring on newer version of Android, since // the user needs to switch back the sms app afterwards return PowerManager.FULL_WAKE_LOCK; } else { return super.wakeLockType(); } } public static boolean isServiceWorking() { return service != null && service.isWorking(); } }