Merge "Add permission checks"
diff --git a/api/current.txt b/api/current.txt
index 4fb9e0a..50579a4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13048,6 +13048,8 @@
method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, android.graphics.Bitmap.Config);
+ method public static android.graphics.Bitmap createBitmap(android.graphics.Picture);
+ method public static android.graphics.Bitmap createBitmap(android.graphics.Picture, int, int, android.graphics.Bitmap.Config);
method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, boolean);
method public int describeContents();
method public void eraseColor(int);
@@ -14105,6 +14107,7 @@
method public void endRecording();
method public int getHeight();
method public int getWidth();
+ method public boolean requiresHardwareAcceleration();
method public deprecated void writeToStream(java.io.OutputStream);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 62cc2a3..ad71e7c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19,6 +19,7 @@
field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
+ field public static final java.lang.String BIND_DATA_SERVICE = "android.permission.BIND_DATA_SERVICE";
field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
@@ -1140,6 +1141,15 @@
package android.hardware.display {
+ public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public float[] getBucketBoundaries();
+ method public java.time.LocalDate getLocalDate();
+ method public float[] getStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 9af80e3..92bf24d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -298,6 +298,15 @@
package android.hardware.display {
+ public final class AmbientBrightnessDayStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public float[] getBucketBoundaries();
+ method public java.time.LocalDate getLocalDate();
+ method public float[] getStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
+ }
+
public final class BrightnessChangeEvent implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 8101b16..b0019ac 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -41,6 +41,8 @@
src/external/ResourceHealthManagerPuller.cpp \
src/external/CpuTimePerUidPuller.cpp \
src/external/CpuTimePerUidFreqPuller.cpp \
+ src/external/KernelUidCpuActiveTimeReader.cpp \
+ src/external/KernelUidCpuClusterTimeReader.cpp \
src/external/StatsPullerManagerImpl.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index f545bb0..4e41454 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -551,6 +551,12 @@
return NO_ERROR;
}
+status_t StatsService::cmd_clear_puller_cache(FILE* out) {
+ mStatsPullerManager.ClearPullerCache();
+ fprintf(out, "Puller cached data removed!\n");
+ return NO_ERROR;
+}
+
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
const vector<String16>& app) {
VLOG("StatsService::informAllUidData was called");
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index be20893..fd3ed1d 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -197,6 +197,11 @@
*/
status_t cmd_dump_memory_info(FILE* out);
+ /*
+ * Clear all puller cached data
+ */
+ status_t cmd_clear_puller_cache(FILE* out);
+
/**
* Update a configuration.
*/
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index cadc535..da14434 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -59,6 +59,11 @@
return ret;
}
+void StatsPuller::ClearCache() {
+ lock_guard<std::mutex> lock(mLock);
+ mCachedData.clear();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 47cc9f0..bc7c45f 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -38,6 +38,8 @@
bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ void ClearCache();
+
protected:
// The atom tag id this puller pulls
const int mTagId;
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 00a1475..4826d96 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -50,10 +50,14 @@
return mPullerManager.Pull(tagId, data);
}
- virtual void SetTimeBaseSec(const long timeBaseSec) {
+ void SetTimeBaseSec(const long timeBaseSec) {
mPullerManager.SetTimeBaseSec(timeBaseSec);
}
+ void ClearPullerCache() {
+ mPullerManager.ClearPullerCache();
+ }
+
private:
StatsPullerManagerImpl
& mPullerManager = StatsPullerManagerImpl::GetInstance();
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index d8e8a41..71b0abe 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -199,6 +199,12 @@
}
}
+void StatsPullerManagerImpl::ClearPullerCache() {
+ for (auto puller : mPullers) {
+ puller.second->ClearCache();
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 7c59f66..fba3ade 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -49,6 +49,8 @@
void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
+ void ClearPullerCache();
+
private:
StatsPullerManagerImpl();
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fdb0ac9..04c44a3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -425,6 +425,12 @@
* safely called after {@link #onPause()} and allows and application to safely
* wait until {@link #onStop()} to save persistent state.</p>
*
+ * <p class="note">For applications targeting platforms starting with
+ * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)}
+ * will always be called after {@link #onStop}, so an application may safely
+ * perform fragment transactions in {@link #onStop} and will be able to save
+ * persistent state later.</p>
+ *
* <p>For those methods that are not marked as being killable, the activity's
* process will not be killed by the system starting from the time the method
* is called and continuing after it returns. Thus an activity is in the killable
@@ -1577,8 +1583,11 @@
* call through to the default implementation, otherwise be prepared to save
* all of the state of each view yourself.
*
- * <p>If called, this method will occur before {@link #onStop}. There are
- * no guarantees about whether it will occur before or after {@link #onPause}.
+ * <p>If called, this method will occur after {@link #onStop} for applications
+ * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
+ * For applications targeting earlier platform versions this method will occur
+ * before {@link #onStop} and there are no guarantees about whether it will
+ * occur before or after {@link #onPause}.
*
* @param outState Bundle in which to place your saved state.
*
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 29e091b..42825f0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -488,12 +488,14 @@
}
}
- public boolean isPreHoneycomb() {
- if (activity != null) {
- return activity.getApplicationInfo().targetSdkVersion
- < android.os.Build.VERSION_CODES.HONEYCOMB;
- }
- return false;
+ private boolean isPreHoneycomb() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.HONEYCOMB;
+ }
+
+ private boolean isPreP() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.P;
}
public boolean isPersistable() {
@@ -4165,9 +4167,12 @@
* {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
*/
private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
+ // Before P onSaveInstanceState was called before onStop, starting with P it's
+ // called after. Before Honeycomb state was always saved before onPause.
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
- if (shouldSaveState) {
+ final boolean isPreP = r.isPreP();
+ if (shouldSaveState && isPreP) {
callActivityOnSaveInstanceState(r);
}
@@ -4186,6 +4191,10 @@
r.setState(ON_STOP);
EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
r.activity.getComponentName().getClassName(), reason);
+
+ if (shouldSaveState && !isPreP) {
+ callActivityOnSaveInstanceState(r);
+ }
}
private void updateVisibility(ActivityClientRecord r, boolean show) {
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
index 41be397..00f3c36 100644
--- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -17,6 +17,8 @@
package android.hardware.display;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,11 +30,12 @@
/**
* AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
* {@see DisplayManager.getAmbientBrightnessStats()}
- * TODO: Make this system API
*
* @hide
*/
-public class AmbientBrightnessDayStats implements Parcelable {
+@SystemApi
+@TestApi
+public final class AmbientBrightnessDayStats implements Parcelable {
/** The localdate for which brightness stats are being tracked */
private final LocalDate mLocalDate;
@@ -48,30 +51,30 @@
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@NonNull float[] bucketBoundaries) {
- Preconditions.checkNotNull(localDate);
- Preconditions.checkNotNull(bucketBoundaries);
- int numBuckets = bucketBoundaries.length;
- if (numBuckets < 1) {
- throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
- }
- mLocalDate = localDate;
- mBucketBoundaries = bucketBoundaries;
- mStats = new float[numBuckets];
+ this(localDate, bucketBoundaries, null);
}
/**
* @hide
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
- @NonNull float[] bucketBoundaries, @NonNull float[] stats) {
+ @NonNull float[] bucketBoundaries, float[] stats) {
Preconditions.checkNotNull(localDate);
Preconditions.checkNotNull(bucketBoundaries);
- Preconditions.checkNotNull(stats);
+ Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
+ "bucketBoundaries");
if (bucketBoundaries.length < 1) {
throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
}
- if (bucketBoundaries.length != stats.length) {
- throw new IllegalArgumentException("Bucket boundaries and stats must be of same size.");
+ checkSorted(bucketBoundaries);
+ if (stats == null) {
+ stats = new float[bucketBoundaries.length];
+ } else {
+ Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
+ if (bucketBoundaries.length != stats.length) {
+ throw new IllegalArgumentException(
+ "Bucket boundaries and stats must be of same size.");
+ }
}
mLocalDate = localDate;
mBucketBoundaries = bucketBoundaries;
@@ -193,4 +196,16 @@
}
return low;
}
+
+ private static void checkSorted(float[] values) {
+ if (values.length <= 1) {
+ return;
+ }
+ float prevValue = values[0];
+ for (int i = 1; i < values.length; i++) {
+ Preconditions.checkState(prevValue < values[i]);
+ prevValue = values[i];
+ }
+ return;
+ }
}
diff --git a/core/java/android/text/style/DrawableMarginSpan.java b/core/java/android/text/style/DrawableMarginSpan.java
index 3524179..cd199b3 100644
--- a/core/java/android/text/style/DrawableMarginSpan.java
+++ b/core/java/android/text/style/DrawableMarginSpan.java
@@ -16,60 +16,100 @@
package android.text.style;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.Spanned;
-public class DrawableMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
- public DrawableMarginSpan(Drawable b) {
- mDrawable = b;
+/**
+ * A span which adds a drawable and a padding to the paragraph it's attached to.
+ * <p>
+ * If the height of the drawable is bigger than the height of the line it's attached to then the
+ * line height is increased to fit the drawable. <code>DrawableMarginSpan</code> allows setting a
+ * padding between the drawable and the text. The default value is 0. The span must be set from the
+ * beginning of the text, otherwise either the span won't be rendered or it will be rendered
+ * incorrectly.
+ * <p>
+ * For example, a drawable and a padding of 20px can be added like this:
+ * <pre>{@code SpannableString string = new SpannableString("Text with a drawable.");
+ * string.setSpan(new DrawableMarginSpan(drawable, 20), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/drawablemarginspan.png" />
+ * <figcaption>Text with a drawable and a padding.</figcaption>
+ * <p>
+ *
+ * @see IconMarginSpan for working with a {@link android.graphics.Bitmap} instead of
+ * a {@link Drawable}.
+ */
+public class DrawableMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+ private static final int STANDARD_PAD_WIDTH = 0;
+
+ @NonNull
+ private final Drawable mDrawable;
+ @Px
+ private final int mPad;
+
+ /**
+ * Creates a {@link DrawableMarginSpan} from a {@link Drawable}. The pad width will be 0.
+ *
+ * @param drawable the drawable to be added
+ */
+ public DrawableMarginSpan(@NonNull Drawable drawable) {
+ this(drawable, STANDARD_PAD_WIDTH);
}
- public DrawableMarginSpan(Drawable b, int pad) {
- mDrawable = b;
+ /**
+ * Creates a {@link DrawableMarginSpan} from a {@link Drawable} and a padding, in pixels.
+ *
+ * @param drawable the drawable to be added
+ * @param pad the distance between the drawable and the text
+ */
+ public DrawableMarginSpan(@NonNull Drawable drawable, int pad) {
+ mDrawable = drawable;
mPad = pad;
}
+ @Override
public int getLeadingMargin(boolean first) {
return mDrawable.getIntrinsicWidth() + mPad;
}
- public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- boolean first, Layout layout) {
+ @Override
+ public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir,
+ int top, int baseline, int bottom,
+ @NonNull CharSequence text, int start, int end,
+ boolean first, @NonNull Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
- int ix = (int)x;
- int itop = (int)layout.getLineTop(layout.getLineForOffset(st));
+ int ix = (int) x;
+ int itop = (int) layout.getLineTop(layout.getLineForOffset(st));
int dw = mDrawable.getIntrinsicWidth();
int dh = mDrawable.getIntrinsicHeight();
// XXX What to do about Paint?
- mDrawable.setBounds(ix, itop, ix+dw, itop+dh);
+ mDrawable.setBounds(ix, itop, ix + dw, itop + dh);
mDrawable.draw(c);
}
- public void chooseHeight(CharSequence text, int start, int end,
- int istartv, int v,
- Paint.FontMetricsInt fm) {
+ @Override
+ public void chooseHeight(@NonNull CharSequence text, int start, int end,
+ int istartv, int v,
+ @NonNull Paint.FontMetricsInt fm) {
if (end == ((Spanned) text).getSpanEnd(this)) {
int ht = mDrawable.getIntrinsicHeight();
int need = ht - (v + fm.descent - fm.ascent - istartv);
- if (need > 0)
+ if (need > 0) {
fm.descent += need;
+ }
need = ht - (v + fm.bottom - fm.top - istartv);
- if (need > 0)
+ if (need > 0) {
fm.bottom += need;
+ }
}
}
-
- private Drawable mDrawable;
- private int mPad;
}
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index 5b8a6dd..1b16f33 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -16,6 +16,9 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -24,32 +27,71 @@
import java.lang.ref.WeakReference;
/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text.
+ * <p>
+ * For an implementation that constructs the drawable from various sources (<code>Bitmap</code>,
+ * <code>Drawable</code>, resource id or <code>Uri</code>) use {@link ImageSpan}.
+ * <p>
+ * A simple implementation of <code>DynamicDrawableSpan</code> that uses drawables from resources
+ * looks like this:
+ * <pre>
+ * class MyDynamicDrawableSpan extends DynamicDrawableSpan {
*
+ * private final Context mContext;
+ * private final int mResourceId;
+ *
+ * public MyDynamicDrawableSpan(Context context, @DrawableRes int resourceId) {
+ * mContext = context;
+ * mResourceId = resourceId;
+ * }
+ *
+ * {@literal @}Override
+ * public Drawable getDrawable() {
+ * Drawable drawable = mContext.getDrawable(mResourceId);
+ * drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ * return drawable;
+ * }
+ * }</pre>
+ * The class can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a drawable span");
+ * string.setSpan(new MyDynamicDrawableSpan(context, R.mipmap.ic_launcher), 12, 20, Spanned
+ * .SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/dynamicdrawablespan.png" />
+ * <figcaption>Replacing text with a drawable.</figcaption>
*/
public abstract class DynamicDrawableSpan extends ReplacementSpan {
- private static final String TAG = "DynamicDrawableSpan";
-
+
/**
* A constant indicating that the bottom of this span should be aligned
* with the bottom of the surrounding text, i.e., at the same level as the
* lowest descender in the text.
*/
public static final int ALIGN_BOTTOM = 0;
-
+
/**
* A constant indicating that the bottom of this span should be aligned
* with the baseline of the surrounding text.
*/
public static final int ALIGN_BASELINE = 1;
-
+
protected final int mVerticalAlignment;
-
+
+ private WeakReference<Drawable> mDrawableRef;
+
+ /**
+ * Creates a {@link DynamicDrawableSpan}. The default vertical alignment is
+ * {@link #ALIGN_BOTTOM}
+ */
public DynamicDrawableSpan() {
mVerticalAlignment = ALIGN_BOTTOM;
}
/**
- * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}.
+ * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\
+ *
+ * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}
*/
protected DynamicDrawableSpan(int verticalAlignment) {
mVerticalAlignment = verticalAlignment;
@@ -64,22 +106,22 @@
}
/**
- * Your subclass must implement this method to provide the bitmap
+ * Your subclass must implement this method to provide the bitmap
* to be drawn. The dimensions of the bitmap must be the same
* from each call to the next.
*/
public abstract Drawable getDrawable();
@Override
- public int getSize(Paint paint, CharSequence text,
- int start, int end,
- Paint.FontMetricsInt fm) {
+ public int getSize(@NonNull Paint paint, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+ @Nullable Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();
if (fm != null) {
- fm.ascent = -rect.bottom;
- fm.descent = 0;
+ fm.ascent = -rect.bottom;
+ fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
@@ -89,12 +131,12 @@
}
@Override
- public void draw(Canvas canvas, CharSequence text,
- int start, int end, float x,
- int top, int y, int bottom, Paint paint) {
+ public void draw(@NonNull Canvas canvas, CharSequence text,
+ @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
+ int top, int y, int bottom, @NonNull Paint paint) {
Drawable b = getCachedDrawable();
canvas.save();
-
+
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
transY -= paint.getFontMetricsInt().descent;
@@ -109,8 +151,9 @@
WeakReference<Drawable> wr = mDrawableRef;
Drawable d = null;
- if (wr != null)
+ if (wr != null) {
d = wr.get();
+ }
if (d == null) {
d = getDrawable();
@@ -119,7 +162,5 @@
return d;
}
-
- private WeakReference<Drawable> mDrawableRef;
}
diff --git a/core/java/android/text/style/IconMarginSpan.java b/core/java/android/text/style/IconMarginSpan.java
index 304c83f..ad78bd5 100644
--- a/core/java/android/text/style/IconMarginSpan.java
+++ b/core/java/android/text/style/IconMarginSpan.java
@@ -16,57 +16,98 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Layout;
import android.text.Spanned;
-public class IconMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
- public IconMarginSpan(Bitmap b) {
- mBitmap = b;
+/**
+ * Paragraph affecting span, that draws a bitmap at the beginning of a text. The span also allows
+ * setting a padding between the bitmap and the text. The default value of the padding is 0px. The
+ * span should be attached from the first character of the text.
+ * <p>
+ * For example, an <code>IconMarginSpan</code> with a bitmap and a padding of 30px can be set
+ * like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with icon and padding");
+ * string.setSpan(new IconMarginSpan(bitmap, 30), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/iconmarginspan.png" />
+ * <figcaption>Text with <code>IconMarginSpan</code></figcaption>
+ * <p>
+ *
+ * @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
+ * a {@link Bitmap}.
+ */
+public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+
+ @NonNull
+ private final Bitmap mBitmap;
+ @Px
+ private final int mPad;
+
+ /**
+ * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+ *
+ * @param bitmap bitmap to be rendered at the beginning of the text
+ */
+ public IconMarginSpan(@NonNull Bitmap bitmap) {
+ this(bitmap, 0);
}
- public IconMarginSpan(Bitmap b, int pad) {
- mBitmap = b;
+ /**
+ * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+ *
+ * @param bitmap bitmap to be rendered at the beginning of the text
+ * @param pad padding width, in pixels, between the bitmap and the text
+ */
+ public IconMarginSpan(@NonNull Bitmap bitmap, @IntRange(from = 0) int pad) {
+ mBitmap = bitmap;
mPad = pad;
}
+ @Override
public int getLeadingMargin(boolean first) {
return mBitmap.getWidth() + mPad;
}
+ @Override
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
- int top, int baseline, int bottom,
- CharSequence text, int start, int end,
- boolean first, Layout layout) {
+ int top, int baseline, int bottom,
+ CharSequence text, int start, int end,
+ boolean first, Layout layout) {
int st = ((Spanned) text).getSpanStart(this);
int itop = layout.getLineTop(layout.getLineForOffset(st));
- if (dir < 0)
+ if (dir < 0) {
x -= mBitmap.getWidth();
+ }
c.drawBitmap(mBitmap, x, itop, p);
}
+ @Override
public void chooseHeight(CharSequence text, int start, int end,
- int istartv, int v,
- Paint.FontMetricsInt fm) {
+ int istartv, int v,
+ Paint.FontMetricsInt fm) {
if (end == ((Spanned) text).getSpanEnd(this)) {
int ht = mBitmap.getHeight();
int need = ht - (v + fm.descent - fm.ascent - istartv);
- if (need > 0)
+ if (need > 0) {
fm.descent += need;
+ }
need = ht - (v + fm.bottom - fm.top - istartv);
- if (need > 0)
+ if (need > 0) {
fm.bottom += need;
+ }
}
}
- private Bitmap mBitmap;
- private int mPad;
}
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index b0bff68..95f0b43 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -17,6 +17,8 @@
package android.text.style;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -27,18 +29,49 @@
import java.io.InputStream;
+/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text. The drawable can be constructed from
+ * varied sources:
+ * <ul>
+ * <li>{@link Bitmap} - see {@link #ImageSpan(Context, Bitmap)} and
+ * {@link #ImageSpan(Context, Bitmap, int)}
+ * </li>
+ * <li>{@link Drawable} - see {@link #ImageSpan(Drawable, int)}</li>
+ * <li>resource id - see {@link #ImageSpan(Context, int, int)}</li>
+ * <li>{@link Uri} - see {@link #ImageSpan(Context, Uri, int)}</li>
+ * </ul>
+ * The default value for the vertical alignment is {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ * <p>
+ * For example, an <code>ImagedSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = SpannableString("Bottom: span.\nBaseline: span.");
+ * // using the default alignment: ALIGN_BOTTOM
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher), 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher, DynamicDrawableSpan.ALIGN_BASELINE),
+ * 22, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/imagespan.png" />
+ * <figcaption>Text with <code>ImageSpan</code>s aligned bottom and baseline.</figcaption>
+ */
public class ImageSpan extends DynamicDrawableSpan {
+
+ @Nullable
private Drawable mDrawable;
+ @Nullable
private Uri mContentUri;
+ @DrawableRes
private int mResourceId;
+ @Nullable
private Context mContext;
+ @Nullable
private String mSource;
/**
* @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead.
*/
@Deprecated
- public ImageSpan(Bitmap b) {
+ public ImageSpan(@NonNull Bitmap b) {
this(null, b, ALIGN_BOTTOM);
}
@@ -46,80 +79,143 @@
* @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead.
*/
@Deprecated
- public ImageSpan(Bitmap b, int verticalAlignment) {
+ public ImageSpan(@NonNull Bitmap b, int verticalAlignment) {
this(null, b, verticalAlignment);
}
- public ImageSpan(Context context, Bitmap b) {
- this(context, b, ALIGN_BOTTOM);
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Bitmap} with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on the display
+ * metrics of the resources
+ * @param bitmap bitmap to be rendered
+ */
+ public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap) {
+ this(context, bitmap, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Bitmap} and a vertical
+ * alignment.
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on
+ * the display metrics of the resources
+ * @param bitmap bitmap to be rendered
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, Bitmap b, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mDrawable = context != null
- ? new BitmapDrawable(context.getResources(), b)
- : new BitmapDrawable(b);
+ ? new BitmapDrawable(context.getResources(), bitmap)
+ : new BitmapDrawable(bitmap);
int width = mDrawable.getIntrinsicWidth();
int height = mDrawable.getIntrinsicHeight();
- mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
- }
-
- public ImageSpan(Drawable d) {
- this(d, ALIGN_BOTTOM);
+ mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
}
/**
- * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * Constructs an {@link ImageSpan} from a drawable with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}.
+ *
+ * @param drawable drawable to be rendered
*/
- public ImageSpan(Drawable d, int verticalAlignment) {
- super(verticalAlignment);
- mDrawable = d;
- }
-
- public ImageSpan(Drawable d, String source) {
- this(d, source, ALIGN_BOTTOM);
+ public ImageSpan(@NonNull Drawable drawable) {
+ this(drawable, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a drawable and a vertical alignment.
+ *
+ * @param drawable drawable to be rendered
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Drawable d, String source, int verticalAlignment) {
+ public ImageSpan(@NonNull Drawable drawable, int verticalAlignment) {
super(verticalAlignment);
- mDrawable = d;
+ mDrawable = drawable;
+ }
+
+ /**
+ * Constructs an {@link ImageSpan} from a drawable and a source with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param drawable drawable to be rendered
+ * @param source drawable's Uri source
+ */
+ public ImageSpan(@NonNull Drawable drawable, @NonNull String source) {
+ this(drawable, source, ALIGN_BOTTOM);
+ }
+
+ /**
+ * Constructs an {@link ImageSpan} from a drawable, a source and a vertical alignment.
+ *
+ * @param drawable drawable to be rendered
+ * @param source drawable's uri source
+ * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
+ */
+ public ImageSpan(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment) {
+ super(verticalAlignment);
+ mDrawable = drawable;
mSource = source;
}
- public ImageSpan(Context context, Uri uri) {
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Uri} with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. The Uri source can be retrieved via
+ * {@link #getSource()}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on the display
+ * metrics of the resources
+ * @param uri {@link Uri} used to construct the drawable that will be rendered
+ */
+ public ImageSpan(@NonNull Context context, @NonNull Uri uri) {
this(context, uri, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Uri} and a vertical
+ * alignment. The Uri source can be retrieved via {@link #getSource()}
+ *
+ * @param context context used to create a drawable from {@param bitmap} based on
+ * the display
+ * metrics of the resources
+ * @param uri {@link Uri} used to construct the drawable that will be rendered.
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, Uri uri, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @NonNull Uri uri, int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mContentUri = uri;
mSource = uri.toString();
}
- public ImageSpan(Context context, @DrawableRes int resourceId) {
+ /**
+ * Constructs an {@link ImageSpan} from a {@link Context} and a resource id with the default
+ * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ *
+ * @param context context used to retrieve the drawable from resources
+ * @param resourceId drawable resource id based on which the drawable is retrieved
+ */
+ public ImageSpan(@NonNull Context context, @DrawableRes int resourceId) {
this(context, resourceId, ALIGN_BOTTOM);
}
/**
+ * Constructs an {@link ImageSpan} from a {@link Context}, a resource id and a vertical
+ * alignment.
+ *
+ * @param context context used to retrieve the drawable from resources
+ * @param resourceId drawable resource id based on which the drawable is retrieved.
* @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
- * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+ * {@link DynamicDrawableSpan#ALIGN_BASELINE}
*/
- public ImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment) {
+ public ImageSpan(@NonNull Context context, @DrawableRes int resourceId,
+ int verticalAlignment) {
super(verticalAlignment);
mContext = context;
mResourceId = resourceId;
@@ -128,10 +224,10 @@
@Override
public Drawable getDrawable() {
Drawable drawable = null;
-
+
if (mDrawable != null) {
drawable = mDrawable;
- } else if (mContentUri != null) {
+ } else if (mContentUri != null) {
Bitmap bitmap = null;
try {
InputStream is = mContext.getContentResolver().openInputStream(
@@ -142,7 +238,7 @@
drawable.getIntrinsicHeight());
is.close();
} catch (Exception e) {
- Log.e("sms", "Failed to loaded content " + mContentUri, e);
+ Log.e("ImageSpan", "Failed to loaded content " + mContentUri, e);
}
} else {
try {
@@ -150,8 +246,8 @@
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
} catch (Exception e) {
- Log.e("sms", "Unable to find resource: " + mResourceId);
- }
+ Log.e("ImageSpan", "Unable to find resource: " + mResourceId);
+ }
}
return drawable;
@@ -159,9 +255,12 @@
/**
* Returns the source string that was saved during construction.
+ *
+ * @return the source string that was saved during construction
+ * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)}
*/
+ @Nullable
public String getSource() {
return mSource;
}
-
}
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 1ebee82..50ee5f3 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -19,16 +19,42 @@
import android.graphics.Paint;
import android.text.TextPaint;
-public interface LineHeightSpan
-extends ParagraphStyle, WrapTogetherSpan
-{
+/**
+ * The classes that affect the height of the line should implement this interface.
+ */
+public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
+ /**
+ * Classes that implement this should define how the height is being calculated.
+ *
+ * @param text the text
+ * @param start the start of the line
+ * @param end the end of the line
+ * @param spanstartv the start of the span
+ * @param lineHeight the line height
+ * @param fm font metrics of the paint, in integers
+ */
public void chooseHeight(CharSequence text, int start, int end,
- int spanstartv, int v,
- Paint.FontMetricsInt fm);
+ int spanstartv, int lineHeight,
+ Paint.FontMetricsInt fm);
+ /**
+ * The classes that affect the height of the line with respect to density, should implement this
+ * interface.
+ */
public interface WithDensity extends LineHeightSpan {
+
+ /**
+ * Classes that implement this should define how the height is being calculated.
+ *
+ * @param text the text
+ * @param start the start of the line
+ * @param end the end of the line
+ * @param spanstartv the start of the span
+ * @param lineHeight the line height
+ * @param paint the paint
+ */
public void chooseHeight(CharSequence text, int start, int end,
- int spanstartv, int v,
- Paint.FontMetricsInt fm, TextPaint paint);
+ int spanstartv, int lineHeight,
+ Paint.FontMetricsInt fm, TextPaint paint);
}
}
diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java
index 2ff52a8..d76ef94 100644
--- a/core/java/android/text/style/MaskFilterSpan.java
+++ b/core/java/android/text/style/MaskFilterSpan.java
@@ -18,15 +18,36 @@
import android.graphics.MaskFilter;
import android.text.TextPaint;
-
+/**
+ * Span that allows setting a {@link MaskFilter} to the text it's attached to.
+ * <p>
+ * For example, to blur a text, a {@link android.graphics.BlurMaskFilter} can be used:
+ * <pre>
+ * MaskFilter blurMask = new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL);
+ * SpannableString string = new SpannableString("Text with blur mask");
+ * string.setSpan(new MaskFilterSpan(blurMask), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" />
+ * <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption>
+ */
public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
private MaskFilter mFilter;
+ /**
+ * Creates a {@link MaskFilterSpan} from a {@link MaskFilter}.
+ *
+ * @param filter the filter to be applied to the <code>TextPaint</code>
+ */
public MaskFilterSpan(MaskFilter filter) {
mFilter = filter;
}
+ /**
+ * Return the mask filter for this span.
+ *
+ * @return the mask filter for this span
+ */
public MaskFilter getMaskFilter() {
return mFilter;
}
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index f900db5..bdfa700 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -24,55 +25,76 @@
import android.text.TextUtils;
/**
- *
- * Describes a style in a span.
+ * Span that allows setting the style of the text it's attached to.
+ * Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and
+ * {@link Typeface#BOLD_ITALIC}.
+ * <p>
* Note that styles are cumulative -- if both bold and italic are set in
* separate spans, or if the base style is bold and a span calls for italic,
* you get bold italic. You can't turn off a style from the base style.
- *
+ * <p>
+ * For example, the <code>StyleSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Bold and italic text");
+ * string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/stylespan.png" />
+ * <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption>
*/
public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mStyle;
/**
- *
+ * Creates a {@link StyleSpan} from a style.
+ *
* @param style An integer constant describing the style for this span. Examples
- * include bold, italic, and normal. Values are constants defined
- * in {@link android.graphics.Typeface}.
+ * include bold, italic, and normal. Values are constants defined
+ * in {@link Typeface}.
*/
public StyleSpan(int style) {
mStyle = style;
}
- public StyleSpan(Parcel src) {
+ /**
+ * Creates a {@link StyleSpan} from a parcel.
+ *
+ * @param src the parcel
+ */
+ public StyleSpan(@NonNull Parcel src) {
mStyle = src.readInt();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.STYLE_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeInt(mStyle);
}
/**
- * Returns the style constant defined in {@link android.graphics.Typeface}.
+ * Returns the style constant defined in {@link Typeface}.
*/
public int getStyle() {
return mStyle;
diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java
index 0566428..2cceb2c 100644
--- a/core/java/android/text/style/TabStopSpan.java
+++ b/core/java/android/text/style/TabStopSpan.java
@@ -16,39 +16,54 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.Px;
+
/**
- * Represents a single tab stop on a line.
+ * Paragraph affecting span that changes the position of the tab with respect to
+ * the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab
+ * encountered on the first line of the text.
*/
-public interface TabStopSpan
-extends ParagraphStyle
-{
- /**
- * Returns the offset of the tab stop from the leading margin of the
- * line.
- * @return the offset
- */
- public int getTabStop();
+public interface TabStopSpan extends ParagraphStyle {
/**
- * The default implementation of TabStopSpan.
+ * Returns the offset of the tab stop from the leading margin of the line, in pixels.
+ *
+ * @return the offset, in pixels
*/
- public static class Standard
- implements TabStopSpan
- {
+ int getTabStop();
+
+ /**
+ * The default implementation of TabStopSpan that allows setting the offset of the tab stop
+ * from the leading margin of the first line of text.
+ * <p>
+ * Let's consider that we have the following text: <i>"\tParagraph text beginning with tab."</i>
+ * and we want to move the tab stop with 100px. This can be achieved like this:
+ * <pre>
+ * SpannableString string = new SpannableString("\tParagraph text beginning with tab.");
+ * string.setSpan(new TabStopSpan.Standard(100), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/tabstopspan.png" />
+ * <figcaption>Text with a tab stop and a <code>TabStopSpan</code></figcaption>
+ */
+ class Standard implements TabStopSpan {
+
+ @Px
+ private int mTabOffset;
+
/**
- * Constructor.
+ * Constructs a {@link TabStopSpan.Standard} based on an offset.
*
- * @param where the offset of the tab stop from the leading margin of
- * the line
+ * @param offset the offset of the tab stop from the leading margin of
+ * the line, in pixels
*/
- public Standard(int where) {
- mTab = where;
+ public Standard(@IntRange(from = 0) int offset) {
+ mTabOffset = offset;
}
+ @Override
public int getTabStop() {
- return mTab;
+ return mTabOffset;
}
-
- private int mTab;
}
}
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index aa622d8..1622812 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -24,42 +25,59 @@
import android.text.TextUtils;
/**
- * Changes the typeface family of the text to which the span is attached.
+ * Changes the typeface family of the text to which the span is attached. Examples of typeface
+ * family include "monospace", "serif", and "sans-serif".
+ * <p>
+ * For example, change the typeface of a text to "monospace" like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with typeface span");
+ * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" />
+ * <figcaption>Text with "monospace" typeface family.</figcaption>
*/
public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
+
private final String mFamily;
/**
- * @param family The font family for this typeface. Examples include
- * "monospace", "serif", and "sans-serif".
+ * Constructs a {@link TypefaceSpan} based on a font family.
+ *
+ * @param family The font family for this typeface. Examples include
+ * "monospace", "serif", and "sans-serif".
*/
public TypefaceSpan(String family) {
mFamily = family;
}
- public TypefaceSpan(Parcel src) {
+ public TypefaceSpan(@NonNull Parcel src) {
mFamily = src.readString();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.TYPEFACE_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mFamily);
}
@@ -71,16 +89,16 @@
}
@Override
- public void updateDrawState(TextPaint ds) {
- apply(ds, mFamily);
+ public void updateDrawState(@NonNull TextPaint textPaint) {
+ apply(textPaint, mFamily);
}
@Override
- public void updateMeasureState(TextPaint paint) {
- apply(paint, mFamily);
+ public void updateMeasureState(@NonNull TextPaint textPaint) {
+ apply(textPaint, mFamily);
}
- private static void apply(Paint paint, String family) {
+ private static void apply(@NonNull Paint paint, String family) {
int oldStyle;
Typeface old = paint.getTypeface();
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 58239ef..eab1ef4 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+import android.annotation.NonNull;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -27,40 +28,71 @@
import android.util.Log;
import android.view.View;
+/**
+ * Implementation of the {@link ClickableSpan} that allows setting a url string. When
+ * selecting and clicking on the text to which the span is attached, the <code>URLSpan</code>
+ * will try to open the url, by launching an an Activity with an {@link Intent#ACTION_VIEW} intent.
+ * <p>
+ * For example, a <code>URLSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a url span");
+ * string.setSpan(new URLSpan("http://www.developer.android.com"), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/urlspan.png" />
+ * <figcaption>Text with <code>URLSpan</code>.</figcaption>
+ */
public class URLSpan extends ClickableSpan implements ParcelableSpan {
private final String mURL;
+ /**
+ * Constructs a {@link URLSpan} from a url string.
+ *
+ * @param url the url string
+ */
public URLSpan(String url) {
mURL = url;
}
- public URLSpan(Parcel src) {
+ /**
+ * Constructs a {@link URLSpan} from a parcel.
+ */
+ public URLSpan(@NonNull Parcel src) {
mURL = src.readString();
}
-
+
+ @Override
public int getSpanTypeId() {
return getSpanTypeIdInternal();
}
/** @hide */
+ @Override
public int getSpanTypeIdInternal() {
return TextUtils.URL_SPAN;
}
-
+
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
writeToParcelInternal(dest, flags);
}
/** @hide */
- public void writeToParcelInternal(Parcel dest, int flags) {
+ @Override
+ public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mURL);
}
+ /**
+ * Get the url string for this span.
+ *
+ * @return the url string.
+ */
public String getURL() {
return mURL;
}
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 2e4cccf..5f6d9ce 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.session.MediaController;
import android.media.update.ApiLoader;
@@ -163,14 +162,6 @@
}
/**
- * @hide
- */
- @SystemApi
- public MediaControlView2Provider getProvider() {
- return mProvider;
- }
-
- /**
* Sets MediaController instance to control corresponding MediaSession.
*/
public void setController(MediaController controller) {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index f9a2341..e69a360 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,6 +55,8 @@
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
/** Do not enfore hidden API access restrictions. */
public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Force generation of native debugging information for backtraces. */
+ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 43d5b23..d58b95a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1785,13 +1785,22 @@
<!-- Must be required by an ImsService to ensure that only the
system can bind to it.
- <p>Protection level: signature|privileged
+ <p>Protection level: signature|privileged|vendorPrivileged
@SystemApi
@hide
-->
<permission android:name="android.permission.BIND_IMS_SERVICE"
android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <!-- Must be required by a DataService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_DATA_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
<!-- Allows an application to manage embedded subscriptions (those on a eUICC) through
EuiccManager APIs.
<p>Protection level: signature|privileged|development
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 224503b..a240616 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4815,7 +4815,7 @@
A toast message shown when an app shortcut that was restored from a previous device is clicked,
but it cannot be started because the shortcut was created by a newer version of the app.
-->
- <string name="shortcut_restored_on_lower_version">This shortcut requires latest app</string>
+ <string name="shortcut_restored_on_lower_version">App version downgraded, or isn\u2019t compatible with this shortcut</string>
<!--
A toast message shown when an app shortcut that was restored from a previous device is clicked,
diff --git a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
index 84409d4..f90ae34 100644
--- a/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
+++ b/core/tests/coretests/src/android/hardware/display/AmbientBrightnessDayStatsTest.java
@@ -16,8 +16,11 @@
package android.hardware.display;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
@@ -27,20 +30,53 @@
import org.junit.runner.RunWith;
import java.time.LocalDate;
+import java.util.Arrays;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AmbientBrightnessDayStatsTest {
+ private static final LocalDate LOCAL_DATE = LocalDate.now();
+ private static final float[] BUCKET_BOUNDARIES = {0, 1, 10, 100};
+ private static final float[] STATS = {1.3f, 2.6f, 5.8f, 10};
+
+ @Test
+ public void testParamsMustNotBeNull() {
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(LOCAL_DATE, null));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(null, BUCKET_BOUNDARIES, STATS));
+
+ assertThrows(NullPointerException.class,
+ () -> new AmbientBrightnessDayStats(LOCAL_DATE, null, STATS));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBucketBoundariesMustNotBeEmpty() {
+ new AmbientBrightnessDayStats(LocalDate.now(), new float[]{});
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testStatsAndBoundariesMustHaveSameLength() {
+ float[] stats = Arrays.copyOf(STATS, STATS.length + 1);
+ stats[stats.length - 1] = 0;
+ new AmbientBrightnessDayStats(LOCAL_DATE, BUCKET_BOUNDARIES, stats);
+ }
+
@Test
public void testAmbientBrightnessDayStatsAdd() {
- AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LocalDate.now(),
- new float[]{0, 1, 10, 100});
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES);
dayStats.log(0, 1);
dayStats.log(0.5f, 1.5f);
dayStats.log(50, 12.5f);
dayStats.log(2000, 1.24f);
dayStats.log(-10, 0.5f);
+ assertEquals(4, dayStats.getStats().length);
assertEquals(2.5f, dayStats.getStats()[0], 0);
assertEquals(0, dayStats.getStats()[1], 0);
assertEquals(12.5f, dayStats.getStats()[2], 0);
@@ -48,38 +84,12 @@
}
@Test
- public void testAmbientBrightnessDayStatsEquals() {
- LocalDate today = LocalDate.now();
- AmbientBrightnessDayStats dayStats1 = new AmbientBrightnessDayStats(today,
- new float[]{0, 1, 10, 100});
- AmbientBrightnessDayStats dayStats2 = new AmbientBrightnessDayStats(today,
- new float[]{0, 1, 10, 100}, new float[4]);
- AmbientBrightnessDayStats dayStats3 = new AmbientBrightnessDayStats(today,
- new float[]{0, 1, 10, 100}, new float[]{1, 3, 5, 7});
- AmbientBrightnessDayStats dayStats4 = new AmbientBrightnessDayStats(today,
- new float[]{0, 1, 10, 100}, new float[]{1, 3, 5, 0});
- assertEquals(dayStats1, dayStats2);
- assertEquals(dayStats1.hashCode(), dayStats2.hashCode());
- assertNotEquals(dayStats1, dayStats3);
- assertNotEquals(dayStats1.hashCode(), dayStats3.hashCode());
- dayStats4.log(100, 7);
- assertEquals(dayStats3, dayStats4);
- assertEquals(dayStats3.hashCode(), dayStats4.hashCode());
- }
-
- @Test
- public void testAmbientBrightnessDayStatsIncorrectInit() {
- try {
- new AmbientBrightnessDayStats(LocalDate.now(), new float[]{1, 10, 100},
- new float[]{1, 5, 6, 7});
- } catch (IllegalArgumentException e) {
- // Expected
- }
- try {
- new AmbientBrightnessDayStats(LocalDate.now(), new float[]{});
- } catch (IllegalArgumentException e) {
- // Expected
- }
+ public void testGetters() {
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ assertEquals(LOCAL_DATE, dayStats.getLocalDate());
+ assertArrayEquals(BUCKET_BOUNDARIES, dayStats.getBucketBoundaries(), 0);
+ assertArrayEquals(STATS, dayStats.getStats(), 0);
}
@Test
@@ -100,4 +110,62 @@
parcel);
assertEquals(stats, statsAgain);
}
+
+ @Test
+ public void testAmbientBrightnessDayStatsEquals() {
+ AmbientBrightnessDayStats emptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES);
+ AmbientBrightnessDayStats identicalEmptyDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, new float[BUCKET_BOUNDARIES.length]);
+ assertEquals(emptyDayStats, identicalEmptyDayStats);
+ assertEquals(emptyDayStats.hashCode(), identicalEmptyDayStats.hashCode());
+
+ AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ AmbientBrightnessDayStats identicalDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, STATS);
+ assertEquals(dayStats, identicalDayStats);
+ assertEquals(dayStats.hashCode(), identicalDayStats.hashCode());
+
+ assertNotEquals(emptyDayStats, dayStats);
+ assertNotEquals(emptyDayStats.hashCode(), dayStats.hashCode());
+
+ AmbientBrightnessDayStats differentDateDayStats = new AmbientBrightnessDayStats(
+ LOCAL_DATE.plusDays(1), BUCKET_BOUNDARIES, STATS);
+ assertNotEquals(dayStats, differentDateDayStats);
+ assertNotEquals(dayStats.hashCode(), differentDateDayStats.hashCode());
+
+ float[] differentStats = Arrays.copyOf(STATS, STATS.length);
+ differentStats[differentStats.length - 1] += 5f;
+ AmbientBrightnessDayStats differentStatsDayStats = new AmbientBrightnessDayStats(LOCAL_DATE,
+ BUCKET_BOUNDARIES, differentStats);
+ assertNotEquals(dayStats, differentDateDayStats);
+ assertNotEquals(dayStats.hashCode(), differentStatsDayStats.hashCode());
+
+ float[] differentBucketBoundaries = Arrays.copyOf(BUCKET_BOUNDARIES,
+ BUCKET_BOUNDARIES.length);
+ differentBucketBoundaries[differentBucketBoundaries.length - 1] += 100f;
+ AmbientBrightnessDayStats differentBoundariesDayStats = new AmbientBrightnessDayStats(
+ LOCAL_DATE, differentBucketBoundaries, STATS);
+ assertNotEquals(dayStats, differentBoundariesDayStats);
+ assertNotEquals(dayStats.hashCode(), differentBoundariesDayStats.hashCode());
+ }
+
+ private interface ExceptionRunnable {
+ void run() throws Exception;
+ }
+
+ private static void assertThrows(Class<? extends Throwable> exceptionClass,
+ ExceptionRunnable r) {
+ try {
+ r.run();
+ } catch (Throwable e) {
+ assertTrue("Expected exception type " + exceptionClass.getName() + " but got "
+ + e.getClass().getName(), exceptionClass.isAssignableFrom(e.getClass()));
+ return;
+ }
+ fail("Expected exception type " + exceptionClass.getName()
+ + ", but no exception was thrown");
+ }
+
}
diff --git a/docs/html/reference/images/text/style/drawablemarginspan.png b/docs/html/reference/images/text/style/drawablemarginspan.png
new file mode 100644
index 0000000..edf926d
--- /dev/null
+++ b/docs/html/reference/images/text/style/drawablemarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/dynamicdrawablespan.png b/docs/html/reference/images/text/style/dynamicdrawablespan.png
new file mode 100644
index 0000000..8776b03
--- /dev/null
+++ b/docs/html/reference/images/text/style/dynamicdrawablespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/iconmarginspan.png b/docs/html/reference/images/text/style/iconmarginspan.png
new file mode 100644
index 0000000..8ec39be
--- /dev/null
+++ b/docs/html/reference/images/text/style/iconmarginspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/imagespan.png b/docs/html/reference/images/text/style/imagespan.png
new file mode 100644
index 0000000..c03e6bb
--- /dev/null
+++ b/docs/html/reference/images/text/style/imagespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/maskfilterspan.png b/docs/html/reference/images/text/style/maskfilterspan.png
new file mode 100644
index 0000000..6e55dbc
--- /dev/null
+++ b/docs/html/reference/images/text/style/maskfilterspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/stylespan.png b/docs/html/reference/images/text/style/stylespan.png
new file mode 100644
index 0000000..9ffa05b
--- /dev/null
+++ b/docs/html/reference/images/text/style/stylespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/tabstopspan.png b/docs/html/reference/images/text/style/tabstopspan.png
new file mode 100644
index 0000000..89a1121
--- /dev/null
+++ b/docs/html/reference/images/text/style/tabstopspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/typefacespan.png b/docs/html/reference/images/text/style/typefacespan.png
new file mode 100644
index 0000000..67e2cf9
--- /dev/null
+++ b/docs/html/reference/images/text/style/typefacespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/urlspan.png b/docs/html/reference/images/text/style/urlspan.png
new file mode 100644
index 0000000..1134520
--- /dev/null
+++ b/docs/html/reference/images/text/style/urlspan.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 627d551..69a5874 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -541,10 +541,19 @@
return mAllowHwBitmapsInSwMode;
}
+ /**
+ * @hide
+ */
+ protected void onHwBitmapInSwMode() {
+ if (!mAllowHwBitmapsInSwMode) {
+ throw new IllegalArgumentException(
+ "Software rendering doesn't support hardware bitmaps");
+ }
+ }
+
private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
- if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated()
- && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
- throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
+ if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+ onHwBitmapInSwMode();
}
}
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 0072012..44e7066 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -29,6 +29,10 @@
import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+
import libcore.util.NativeAllocationRegistry;
import java.io.OutputStream;
@@ -1171,6 +1175,82 @@
}
/**
+ * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+ *
+ * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
+ * width and height the same as the Picture's width and height and a Config.HARDWARE
+ * config.
+ *
+ * @param source The recorded {@link Picture} of drawing commands that will be
+ * drawn into the returned Bitmap.
+ * @return An immutable bitmap with a HARDWARE config whose contents are created
+ * from the recorded drawing commands in the Picture source.
+ */
+ public static @NonNull Bitmap createBitmap(@NonNull Picture source) {
+ return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
+ }
+
+ /**
+ * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+ *
+ * The bitmap will be immutable with the given width and height. If the width and height
+ * are not the same as the Picture's width & height, the Picture will be scaled to
+ * fit the given width and height.
+ *
+ * @param source The recorded {@link Picture} of drawing commands that will be
+ * drawn into the returned Bitmap.
+ * @param width The width of the bitmap to create. The picture's width will be
+ * scaled to match if necessary.
+ * @param height The height of the bitmap to create. The picture's height will be
+ * scaled to match if necessary.
+ * @param config The {@link Config} of the created bitmap. If this is null then
+ * the bitmap will be {@link Config#HARDWARE}.
+ *
+ * @return An immutable bitmap with a HARDWARE config whose contents are created
+ * from the recorded drawing commands in the Picture source.
+ */
+ public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height,
+ @NonNull Config config) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width & height must be > 0");
+ }
+ if (config == null) {
+ throw new IllegalArgumentException("Config must not be null");
+ }
+ if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
+ StrictMode.noteSlowCall("GPU readback");
+ }
+ if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
+ final RenderNode node = RenderNode.create("BitmapTemporary", null);
+ node.setLeftTopRightBottom(0, 0, width, height);
+ node.setClipToBounds(false);
+ final DisplayListCanvas canvas = node.start(width, height);
+ if (source.getWidth() != width || source.getHeight() != height) {
+ canvas.scale(width / (float) source.getWidth(),
+ height / (float) source.getHeight());
+ }
+ canvas.drawPicture(source);
+ node.end(canvas);
+ Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
+ if (config != Config.HARDWARE) {
+ bitmap = bitmap.copy(config, false);
+ }
+ return bitmap;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, config);
+ Canvas canvas = new Canvas(bitmap);
+ if (source.getWidth() != width || source.getHeight() != height) {
+ canvas.scale(width / (float) source.getWidth(),
+ height / (float) source.getHeight());
+ }
+ canvas.drawPicture(source);
+ canvas.setBitmap(null);
+ bitmap.makeImmutable();
+ return bitmap;
+ }
+ }
+
+ /**
* Returns an optional array of private data, used by the UI system for
* some bitmaps. Not intended to be called by applications.
*/
@@ -1259,6 +1339,12 @@
return mIsMutable;
}
+ /** @hide */
+ public final void makeImmutable() {
+ // todo mIsMutable = false;
+ // todo nMakeImmutable();
+ }
+
/**
* <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
* When a pixel is pre-multiplied, the RGB components have been multiplied by
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 08eeaff..9ac94d8 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -31,8 +31,9 @@
* be replayed on a hardware accelerated canvas.</p>
*/
public class Picture {
- private Canvas mRecordingCanvas;
+ private PictureCanvas mRecordingCanvas;
private long mNativePicture;
+ private boolean mRequiresHwAcceleration;
private static final int WORKING_STREAM_STORAGE = 16 * 1024;
@@ -78,8 +79,12 @@
* into it.
*/
public Canvas beginRecording(int width, int height) {
+ if (mRecordingCanvas != null) {
+ throw new IllegalStateException("Picture already recording, must call #endRecording()");
+ }
long ni = nativeBeginRecording(mNativePicture, width, height);
- mRecordingCanvas = new RecordingCanvas(this, ni);
+ mRecordingCanvas = new PictureCanvas(this, ni);
+ mRequiresHwAcceleration = false;
return mRecordingCanvas;
}
@@ -91,6 +96,7 @@
*/
public void endRecording() {
if (mRecordingCanvas != null) {
+ mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
mRecordingCanvas = null;
nativeEndRecording(mNativePicture);
}
@@ -113,6 +119,18 @@
}
/**
+ * Indicates whether or not this Picture contains recorded commands that only work when
+ * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only
+ * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true.
+ *
+ * @return true if the Picture can only be drawn to a hardware-accelerated canvas,
+ * false otherwise.
+ */
+ public boolean requiresHardwareAcceleration() {
+ return mRequiresHwAcceleration;
+ }
+
+ /**
* Draw this picture on the canvas.
* <p>
* Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could
@@ -129,6 +147,9 @@
if (mRecordingCanvas != null) {
endRecording();
}
+ if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
+ canvas.onHwBitmapInSwMode();
+ }
nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
}
@@ -164,8 +185,7 @@
if (stream == null) {
throw new NullPointerException();
}
- if (!nativeWriteToStream(mNativePicture, stream,
- new byte[WORKING_STREAM_STORAGE])) {
+ if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
throw new RuntimeException();
}
}
@@ -182,10 +202,11 @@
OutputStream stream, byte[] storage);
private static native void nativeDestructor(long nativePicture);
- private static class RecordingCanvas extends Canvas {
+ private static class PictureCanvas extends Canvas {
private final Picture mPicture;
+ boolean mHoldsHwBitmap;
- public RecordingCanvas(Picture pict, long nativeCanvas) {
+ public PictureCanvas(Picture pict, long nativeCanvas) {
super(nativeCanvas);
mPicture = pict;
}
@@ -202,5 +223,10 @@
}
super.drawPicture(picture);
}
+
+ @Override
+ protected void onHwBitmapInSwMode() {
+ mHoldsHwBitmap = true;
+ }
}
}
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index ebc14c8..091b526 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -182,10 +182,11 @@
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
- std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
- std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
- std::shared_ptr<minikin::FontCollection> collection =
- std::make_shared<minikin::FontCollection>(std::move(family));
+ std::vector<minikin::Font> fonts;
+ fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+
+ std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
+ std::make_shared<minikin::FontFamily>(std::move(fonts)));
Typeface* hwTypeface = new Typeface();
hwTypeface->fFontCollection = collection;
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 66d6f52..2232c25 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -56,8 +56,9 @@
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
- return std::make_shared<minikin::FontFamily>(
- std::vector<minikin::Font>({minikin::Font(std::move(font), minikin::FontStyle())}));
+ std::vector<minikin::Font> fonts;
+ fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+ return std::make_shared<minikin::FontFamily>(std::move(fonts));
}
std::vector<std::shared_ptr<minikin::FontFamily>> makeSingleFamlyVector(const char* fileName) {
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 6fe8975..9a66b07 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -94,7 +94,7 @@
infile = mFile.openRead();
readXml(infile);
} catch (FileNotFoundException e) {
- // No data yet
+ Log.d(TAG, "File doesn't exist or isn't readable yet");
} catch (IOException e) {
Log.e(TAG, "Unable to read channel impressions", e);
} catch (NumberFormatException | XmlPullParserException e) {
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 7c35b48..db48f61 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -325,7 +325,8 @@
int dismiss2 = 777;
String key2 = mAssistant.getKey("pkg2", 2, "channel2");
- String xml = "<assistant version=\"1\">\n"
+ String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<assistant version=\"1\">\n"
+ "<impression-set key=\"" + key1 + "\" "
+ "dismisses=\"" + dismiss1 + "\" views=\"" + views1
+ "\" streak=\"" + streak1 + "\"/>\n"
@@ -377,7 +378,6 @@
mAssistant.insertImpressions(key2, ci2);
mAssistant.insertImpressions(key3, ci3);
-
XmlSerializer serializer = new FastXmlSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index a9e5adf..cdd417f 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -73,15 +73,18 @@
android:id="@+id/volume_row_slider_frame"
android:padding="0dp"
android:layout_width="@dimen/volume_dialog_panel_width"
+ android:layoutDirection="rtl"
android:layout_height="150dp">
<SeekBar
android:id="@+id/volume_row_slider"
+ android:clickable="true"
android:padding="0dp"
android:layout_margin="0dp"
android:layout_width="150dp"
android:layout_height="@dimen/volume_dialog_panel_width"
+ android:layoutDirection="rtl"
android:layout_gravity="center"
- android:rotation="270" />
+ android:rotation="90" />
</FrameLayout>
<com.android.keyguard.AlphaOptimizedImageButton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 653e500..eedc50f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -707,6 +707,9 @@
&& !mLockPatternUtils.isLockScreenDisabled(
KeyguardUpdateMonitor.getCurrentUser()),
mSecondaryDisplayShowing, true /* forceCallbacks */);
+ } else {
+ // The system's keyguard is disabled or missing.
+ setShowingLocked(false, mSecondaryDisplayShowing, true);
}
mStatusBarKeyguardViewManager =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index f50a287..4c69594 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -133,11 +133,11 @@
for (int i = 0; i < rowCount; i++) {
View row = rows.getChildAt(i);
if (to == ROTATION_SEASCAPE) {
- rotateSeekBars(row, to, 180);
- } else if (to == ROTATION_LANDSCAPE) {
rotateSeekBars(row, to, 0);
+ } else if (to == ROTATION_LANDSCAPE) {
+ rotateSeekBars(row, to, 180);
} else {
- rotateSeekBars(row, to, 270);
+ rotateSeekBars(row, to, 90);
}
rotate(row, from, to, true);
}
@@ -309,12 +309,6 @@
return super.getOutlineProvider();
}
- @Override
- public void setPressed(boolean pressed)
- {
- // Ignore presses because it activates the seekbar thumb unnecessarily.
- }
-
public void setOutsideTouchListener(OnClickListener onClickListener) {
mHasOutsideTouch = true;
requestLayout();
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index f6a54af..c77dcc0 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -63,7 +63,7 @@
// Number scans that returned at least one result.
optional int32 num_non_empty_scan_results = 13;
- // Number of scans that were one time.
+ // Number of single scans requests.
optional int32 num_oneshot_scans = 14;
// Number of repeated background scans that were scheduled to the chip.
@@ -376,6 +376,9 @@
// Wifi power statistics
optional WifiPowerStats wifi_power_stats = 92;
+
+ // Number of connectivity single scan requests.
+ optional int32 num_connectivity_oneshot_scans = 93;
}
// Information that gets logged for every WiFi connection.
@@ -1155,4 +1158,4 @@
// Amount of time wifi is in tx (ms)
optional int64 tx_time_ms = 5;
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ea53dfc..f370393 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4027,9 +4027,13 @@
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
- if ("true".equals(genDebugInfoProperty)) {
+ if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
+ String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+ if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+ runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+ }
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 502760a..fd435f9 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -771,7 +771,7 @@
* Called whenever packages change, the user switches, or the secure setting
* is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
*/
- private void rebindServices(boolean forceRebind) {
+ protected void rebindServices(boolean forceRebind) {
if (DEBUG) Slog.d(TAG, "rebindServices");
final int[] userIds = mUserProfiles.getCurrentProfileIds();
final int nUserIds = userIds.length;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a4ac155..b25124a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5689,6 +5689,12 @@
mListeners.unregisterService(removed.service, removed.userid);
}
+ @Override
+ public void onUserUnlocked(int user) {
+ if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
+ rebindServices(true);
+ }
+
public void onNotificationEnqueued(final NotificationRecord r) {
final StatusBarNotification sbn = r.sbn;
TrimCache trimCache = new TrimCache(sbn);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 56d4b7e..857925b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -5149,7 +5149,8 @@
.forAllShortcuts(si -> {
switch (package1DisabledReason) {
case ShortcutInfo.DISABLED_REASON_VERSION_LOWER:
- assertEquals("This shortcut requires latest app",
+ assertEquals("App version downgraded, or isn’t compatible"
+ + " with this shortcut",
si.getDisabledMessage());
break;
case ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH:
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 99eb846..e36586e 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -210,6 +210,9 @@
runCommand(instrumentation, "cmd package set-home-activity --user "
+ instrumentation.getContext().getUserId() + " " + component,
result -> result.contains("Success"));
+ runCommand(instrumentation, "cmd shortcut clear-default-launcher --user "
+ + instrumentation.getContext().getUserId(),
+ result -> result.contains("Success"));
}
public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index dc5ba0c..fe63aa1 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -7,22 +7,21 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
+import android.content.pm.PackageManager;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.util.List;
+
/** @hide */
@SystemApi
@SystemService(Context.WIFI_RTT_SERVICE)
@@ -175,7 +174,8 @@
@Deprecated
@SuppressLint("Doclava125")
public Capabilities getCapabilities() {
- return new Capabilities();
+ throw new UnsupportedOperationException(
+ "getCapabilities is not supported in the adaptation layer");
}
/**
@@ -316,16 +316,7 @@
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public RttCapabilities getRttCapabilities() {
- synchronized (mCapabilitiesLock) {
- if (mRttCapabilities == null) {
- try {
- mRttCapabilities = mService.getRttCapabilities();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return mRttCapabilities;
- }
+ return mRttCapabilities;
}
/** specifies parameters for RTT request */
@@ -972,69 +963,6 @@
}
}
- private boolean rttParamSanity(RttParams params, int index) {
- if (mRttCapabilities == null) {
- if(getRttCapabilities() == null) {
- Log.e(TAG, "Can not get RTT capabilities");
- throw new IllegalStateException("RTT chip is not working");
- }
- }
-
- if (params.deviceType != RTT_PEER_TYPE_AP) {
- return false;
- } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
- RTT_TYPE_TWO_SIDED) {
- Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
- return false;
- } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
- !mRttCapabilities.oneSidedRttSupported) {
- Log.e(TAG, "Request " + index + ": One side RTT is not supported");
- return false;
- } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
- !mRttCapabilities.twoSided11McRttSupported) {
- Log.e(TAG, "Request " + index + ": two side RTT is not supported");
- return false;
- } else if(params.bssid == null || params.bssid.isEmpty()) {
- Log.e(TAG,"No BSSID in params");
- return false;
- } else if ( params.numberBurst != 0 ) {
- Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
- return false;
- } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) {
- Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
- params.numSamplesPerBurst);
- return false;
- } else if (params.numRetriesPerMeasurementFrame < 0 ||
- params.numRetriesPerMeasurementFrame > 3) {
- Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" +
- params.numRetriesPerMeasurementFrame);
- return false;
- } else if(params.numRetriesPerFTMR < 0 ||
- params.numRetriesPerFTMR > 3) {
- Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" +
- params.numRetriesPerFTMR);
- return false;
- } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
- Log.e(TAG, "Request " + index + ": LCI is not supported");
- return false;
- } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
- Log.e(TAG, "Request " + index + ": LCR is not supported");
- return false;
- } else if (params.burstTimeout < 1 ||
- (params.burstTimeout > 11 && params.burstTimeout != 15)){
- Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
- return false;
- } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
- return false;
- } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
- return false;
- }
-
- return true;
- }
-
/**
* Request to start an RTT ranging
*
@@ -1045,24 +973,72 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startRanging(RttParams[] params, RttListener listener) {
- int index = 0;
- for(RttParams rttParam : params) {
- if (!rttParamSanity(rttParam, index)) {
- throw new IllegalArgumentException("RTT Request Parameter Illegal");
- }
- index++;
- }
- validateChannel();
- ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
Log.i(TAG, "Send RTT request to RTT Service");
- mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
- 0, putListener(listener), parcelableParams);
+
+ if (!mNewService.isAvailable()) {
+ listener.onFailure(REASON_NOT_AVAILABLE, "");
+ return;
+ }
+
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ for (RttParams rttParams : params) {
+ if (rttParams.deviceType != RTT_PEER_TYPE_AP) {
+ listener.onFailure(REASON_INVALID_REQUEST, "Only AP peers are supported");
+ return;
+ }
+
+ ScanResult reconstructed = new ScanResult();
+ reconstructed.BSSID = rttParams.bssid;
+ if (rttParams.requestType == RTT_TYPE_TWO_SIDED) {
+ reconstructed.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ }
+ reconstructed.channelWidth = rttParams.channelWidth;
+ reconstructed.frequency = rttParams.frequency;
+ reconstructed.centerFreq0 = rttParams.centerFreq0;
+ reconstructed.centerFreq1 = rttParams.centerFreq1;
+ builder.addResponder(
+ android.net.wifi.rtt.ResponderConfig.fromScanResult(reconstructed));
+ }
+ try {
+ mNewService.startRanging(builder.build(), new RangingResultCallback() {
+ @Override
+ public void onRangingFailure(int code) {
+ int localCode = REASON_UNSPECIFIED;
+ if (code == STATUS_CODE_FAIL_RTT_NOT_AVAILABLE) {
+ localCode = REASON_NOT_AVAILABLE;
+ }
+ listener.onFailure(localCode, "");
+ }
+
+ @Override
+ public void onRangingResults(List<RangingResult> results) {
+ RttResult[] legacyResults = new RttResult[results.size()];
+ int i = 0;
+ for (RangingResult result : results) {
+ legacyResults[i] = new RttResult();
+ legacyResults[i].status = result.getStatus();
+ legacyResults[i].bssid = result.getMacAddress().toString();
+ legacyResults[i].distance = result.getDistanceMm() / 10;
+ legacyResults[i].distanceStandardDeviation =
+ result.getDistanceStdDevMm() / 10;
+ legacyResults[i].rssi = result.getRssi();
+ legacyResults[i].ts = result.getRangingTimestampUs();
+ }
+ listener.onSuccess(legacyResults);
+ }
+ }, null);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "startRanging: invalid arguments - " + e);
+ listener.onFailure(REASON_INVALID_REQUEST, e.getMessage());
+ } catch (SecurityException e) {
+ Log.e(TAG, "startRanging: security exception - " + e);
+ listener.onFailure(REASON_PERMISSION_DENIED, e.getMessage());
+ }
}
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void stopRanging(RttListener listener) {
- validateChannel();
- mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+ Log.e(TAG, "stopRanging: unsupported operation - nop");
}
/**
@@ -1095,12 +1071,8 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void enableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = putListenerIfAbsent(callback);
- mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "enableResponder is not supported in the adaptation layer");
}
/**
@@ -1115,16 +1087,8 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void disableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = removeListener(callback);
- if (key == INVALID_KEY) {
- Log.e(TAG, "responder not enabled yet");
- return;
- }
- mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "disableResponder is not supported in the adaptation layer");
}
/**
@@ -1238,17 +1202,9 @@
/** @hide */
public static final int CMD_OP_REG_BINDER = BASE + 9;
- private static final int INVALID_KEY = 0;
-
private final Context mContext;
- private final IRttManager mService;
- private final SparseArray mListenerMap = new SparseArray();
- private final Object mListenerMapLock = new Object();
- private final Object mCapabilitiesLock = new Object();
-
+ private final WifiRttManager mNewService;
private RttCapabilities mRttCapabilities;
- private int mListenerKey = 1;
- private AsyncChannel mAsyncChannel;
/**
* Create a new WifiScanner instance.
@@ -1263,170 +1219,20 @@
*/
public RttManager(Context context, IRttManager service, Looper looper) {
mContext = context;
- mService = service;
- Messenger messenger = null;
- int[] key = new int[1];
- try {
- Log.d(TAG, "Get the messenger from " + mService);
- messenger = mService.getMessenger(new Binder(), key);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
- if (messenger == null) {
- throw new IllegalStateException("getMessenger() returned null! This is invalid.");
- }
+ boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT);
- mAsyncChannel = new AsyncChannel();
-
- Handler handler = new ServiceHandler(looper);
- mAsyncChannel.connectSync(mContext, handler, messenger);
- // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
- // synchronously, which causes RttService to receive the wrong replyTo value.
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION,
- new RttClient(context.getPackageName()));
- mAsyncChannel.sendMessage(CMD_OP_REG_BINDER, key[0]);
+ mRttCapabilities = new RttCapabilities();
+ mRttCapabilities.oneSidedRttSupported = rttSupported;
+ mRttCapabilities.twoSided11McRttSupported = rttSupported;
+ mRttCapabilities.lciSupported = false;
+ mRttCapabilities.lcrSupported = false;
+ mRttCapabilities.preambleSupported = PREAMBLE_HT | PREAMBLE_VHT;
+ mRttCapabilities.bwSupported = RTT_BW_40_SUPPORT | RTT_BW_80_SUPPORT;
+ mRttCapabilities.responderSupported = false;
+ mRttCapabilities.secureRttSupported = false;
}
-
- private void validateChannel() {
- if (mAsyncChannel == null) throw new IllegalStateException(
- "No permission to access and change wifi or a bad initialization");
- }
-
- private int putListener(Object listener) {
- if (listener == null) return INVALID_KEY;
- int key;
- synchronized (mListenerMapLock) {
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- }
- return key;
- }
-
- // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
- private int putListenerIfAbsent(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int key = getListenerKey(listener);
- if (key != INVALID_KEY) {
- return key;
- }
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- return key;
- }
-
- }
-
- private Object getListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- return listener;
- }
- }
-
- private int getListenerKey(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int index = mListenerMap.indexOfValue(listener);
- if (index == -1) {
- return INVALID_KEY;
- } else {
- return mListenerMap.keyAt(index);
- }
- }
- }
-
- private Object removeListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- mListenerMap.remove(key);
- return listener;
- }
- }
-
- private int removeListener(Object listener) {
- int key = getListenerKey(listener);
- if (key == INVALID_KEY) return key;
- synchronized (mListenerMapLock) {
- mListenerMap.remove(key);
- return key;
- }
- }
-
- private class ServiceHandler extends Handler {
- ServiceHandler(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- Log.i(TAG, "RTT manager get message: " + msg.what);
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- return;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel connection lost");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- mAsyncChannel = null;
- getLooper().quit();
- return;
- }
-
- Object listener = getListener(msg.arg2);
- if (listener == null) {
- Log.e(TAG, "invalid listener key = " + msg.arg2 );
- return;
- } else {
- Log.i(TAG, "listener key = " + msg.arg2);
- }
-
- switch (msg.what) {
- /* ActionListeners grouped together */
- case CMD_OP_SUCCEEDED :
- reportSuccess(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_FAILED :
- reportFailure(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_ABORTED :
- ((RttListener) listener).onAborted();
- removeListener(msg.arg2);
- break;
- case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
- ResponderConfig config = (ResponderConfig) msg.obj;
- ((ResponderCallback) (listener)).onResponderEnabled(config);
- break;
- case CMD_OP_ENALBE_RESPONDER_FAILED:
- ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
- removeListener(msg.arg2);
- break;
- default:
- if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
- return;
- }
- }
-
- void reportSuccess(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
- ((RttListener) listener).onSuccess(parcelableResults.mResults);
- }
-
- void reportFailure(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- Bundle bundle = (Bundle) msg.obj;
- ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
- }
- }
-
}