Skip to content

Commit a5a0687

Browse files
author
Adam Cohen
committed
Prevent crash in AppWidgetHost that can be triggered by an AppWidget
-> When the RemoteViewsFactory violates the getViewTypeCount() contract we detect it, and prevent the AdapterView from crashing -> Also made RemoteViewsCache a static inner class, since we may need that down the road, and there's no reason it shouldn't be Change-Id: I872a255167aac94513e522924179de61286b995a
1 parent a4d4df1 commit a5a0687

File tree

1 file changed

+54
-27
lines changed

1 file changed

+54
-27
lines changed

core/java/android/widget/RemoteViewsAdapter.java

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public synchronized boolean isConnected() {
246246
* A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
247247
* they are loaded.
248248
*/
249-
private class RemoteViewsFrameLayout extends FrameLayout {
249+
private static class RemoteViewsFrameLayout extends FrameLayout {
250250
public RemoteViewsFrameLayout(Context context) {
251251
super(context);
252252
}
@@ -301,7 +301,7 @@ public void add(int position, RemoteViewsFrameLayout layout) {
301301
* Notifies each of the RemoteViewsFrameLayouts associated with a particular position that
302302
* the associated RemoteViews has loaded.
303303
*/
304-
public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) {
304+
public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) {
305305
if (view == null) return;
306306

307307
final Integer pos = position;
@@ -331,7 +331,7 @@ public void clear() {
331331
/**
332332
* The meta-data associated with the cache in it's current state.
333333
*/
334-
private class RemoteViewsMetaData {
334+
private static class RemoteViewsMetaData {
335335
int count;
336336
int viewTypeCount;
337337
boolean hasStableIds;
@@ -390,14 +390,23 @@ public int getMappedViewType(int typeId) {
390390
}
391391
}
392392

393+
public boolean isViewTypeInRange(int typeId) {
394+
int mappedType = getMappedViewType(typeId);
395+
if (mappedType >= viewTypeCount) {
396+
return false;
397+
} else {
398+
return true;
399+
}
400+
}
401+
393402
private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
394-
ViewGroup parent) {
403+
ViewGroup parent, Object lock, LayoutInflater layoutInflater) {
395404
// Create and return a new FrameLayout, and setup the references for this position
396405
final Context context = parent.getContext();
397406
RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context);
398407

399408
// Create a new loading view
400-
synchronized (mCache) {
409+
synchronized (lock) {
401410
boolean customLoadingViewAvailable = false;
402411

403412
if (mUserLoadingView != null) {
@@ -425,7 +434,7 @@ private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
425434
mFirstViewHeight = firstView.getMeasuredHeight();
426435
mFirstView = null;
427436
} catch (Exception e) {
428-
float density = mContext.getResources().getDisplayMetrics().density;
437+
float density = context.getResources().getDisplayMetrics().density;
429438
mFirstViewHeight = (int)
430439
Math.round(sDefaultLoadingViewHeight * density);
431440
mFirstView = null;
@@ -434,7 +443,7 @@ private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
434443
}
435444

436445
// Compose the loading view text
437-
TextView loadingTextView = (TextView) mLayoutInflater.inflate(
446+
TextView loadingTextView = (TextView) layoutInflater.inflate(
438447
com.android.internal.R.layout.remote_views_adapter_default_loading_view,
439448
layout, false);
440449
loadingTextView.setHeight(mFirstViewHeight);
@@ -451,7 +460,7 @@ private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
451460
/**
452461
* The meta-data associated with a single item in the cache.
453462
*/
454-
private class RemoteViewsIndexMetaData {
463+
private static class RemoteViewsIndexMetaData {
455464
int typeId;
456465
long itemId;
457466
boolean isRequested;
@@ -462,18 +471,19 @@ public RemoteViewsIndexMetaData(RemoteViews v, long itemId, boolean requested) {
462471

463472
public void set(RemoteViews v, long id, boolean requested) {
464473
itemId = id;
465-
if (v != null)
474+
if (v != null) {
466475
typeId = v.getLayoutId();
467-
else
476+
} else {
468477
typeId = 0;
478+
}
469479
isRequested = requested;
470480
}
471481
}
472482

473483
/**
474484
*
475485
*/
476-
private class FixedSizeRemoteViewsCache {
486+
private static class FixedSizeRemoteViewsCache {
477487
private static final String TAG = "FixedSizeRemoteViewsCache";
478488

479489
// The meta data related to all the RemoteViews, ie. count, is stable, etc.
@@ -861,21 +871,36 @@ private void updateRemoteViews(final int position, boolean isRequested, boolean
861871
"returned from RemoteViewsFactory.");
862872
return;
863873
}
874+
875+
int layoutId = remoteViews.getLayoutId();
876+
RemoteViewsMetaData metaData = mCache.getMetaData();
877+
boolean viewTypeInRange;
878+
synchronized (metaData) {
879+
viewTypeInRange = metaData.isViewTypeInRange(layoutId);
880+
}
864881
synchronized (mCache) {
865-
// Cache the RemoteViews we loaded
866-
mCache.insert(position, remoteViews, itemId, isRequested);
867-
868-
// Notify all the views that we have previously returned for this index that
869-
// there is new data for it.
870-
final RemoteViews rv = remoteViews;
871-
final int typeId = mCache.getMetaDataAt(position).typeId;
872-
if (notifyWhenLoaded) {
873-
mMainQueue.post(new Runnable() {
874-
@Override
875-
public void run() {
876-
mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
877-
}
878-
});
882+
if (viewTypeInRange) {
883+
// Cache the RemoteViews we loaded
884+
mCache.insert(position, remoteViews, itemId, isRequested);
885+
886+
// Notify all the views that we have previously returned for this index that
887+
// there is new data for it.
888+
final RemoteViews rv = remoteViews;
889+
if (notifyWhenLoaded) {
890+
mMainQueue.post(new Runnable() {
891+
@Override
892+
public void run() {
893+
mRequestedViews.notifyOnRemoteViewsLoaded(position, rv);
894+
}
895+
});
896+
}
897+
} else {
898+
// We need to log an error here, as the the view type count specified by the
899+
// factory is less than the number of view types returned. We don't return this
900+
// view to the AdapterView, as this will cause an exception in the hosting process,
901+
// which contains the associated AdapterView.
902+
Log.e(TAG, "Error: widget's RemoteViewsFactory returns more view types than " +
903+
" indicated by getViewTypeCount() ");
879904
}
880905
}
881906
}
@@ -1010,7 +1035,8 @@ public View getView(int position, View convertView, ViewGroup parent) {
10101035
RemoteViewsFrameLayout loadingView = null;
10111036
final RemoteViewsMetaData metaData = mCache.getMetaData();
10121037
synchronized (metaData) {
1013-
loadingView = metaData.createLoadingView(position, convertView, parent);
1038+
loadingView = metaData.createLoadingView(position, convertView, parent,
1039+
mCache, mLayoutInflater);
10141040
}
10151041
return loadingView;
10161042
} finally {
@@ -1022,7 +1048,8 @@ public View getView(int position, View convertView, ViewGroup parent) {
10221048
RemoteViewsFrameLayout loadingView = null;
10231049
final RemoteViewsMetaData metaData = mCache.getMetaData();
10241050
synchronized (metaData) {
1025-
loadingView = metaData.createLoadingView(position, convertView, parent);
1051+
loadingView = metaData.createLoadingView(position, convertView, parent,
1052+
mCache, mLayoutInflater);
10261053
}
10271054

10281055
mRequestedViews.add(position, loadingView);

0 commit comments

Comments
 (0)